18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/******************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Driver for Option High Speed Mobile Devices.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Option International
78c2ecf20Sopenharmony_ci *                     Filip Aben <f.aben@option.com>
88c2ecf20Sopenharmony_ci *                     Denis Joseph Barrow <d.barow@option.com>
98c2ecf20Sopenharmony_ci *                     Jan Dumon <j.dumon@option.com>
108c2ecf20Sopenharmony_ci *  Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd)
118c2ecf20Sopenharmony_ci *  			<ajb@spheresystems.co.uk>
128c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
138c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Novell, Inc.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *****************************************************************************/
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/******************************************************************************
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Description of the device:
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Interface 0:	Contains the IP network interface on the bulk end points.
228c2ecf20Sopenharmony_ci *		The multiplexed serial ports are using the interrupt and
238c2ecf20Sopenharmony_ci *		control endpoints.
248c2ecf20Sopenharmony_ci *		Interrupt contains a bitmap telling which multiplexed
258c2ecf20Sopenharmony_ci *		serialport needs servicing.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Interface 1:	Diagnostics port, uses bulk only, do not submit urbs until the
288c2ecf20Sopenharmony_ci *		port is opened, as this have a huge impact on the network port
298c2ecf20Sopenharmony_ci *		throughput.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Interface 2:	Standard modem interface - circuit switched interface, this
328c2ecf20Sopenharmony_ci *		can be used to make a standard ppp connection however it
338c2ecf20Sopenharmony_ci *              should not be used in conjunction with the IP network interface
348c2ecf20Sopenharmony_ci *              enabled for USB performance reasons i.e. if using this set
358c2ecf20Sopenharmony_ci *              ideally disable_net=1.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci *****************************************************************************/
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
428c2ecf20Sopenharmony_ci#include <linux/slab.h>
438c2ecf20Sopenharmony_ci#include <linux/init.h>
448c2ecf20Sopenharmony_ci#include <linux/delay.h>
458c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
468c2ecf20Sopenharmony_ci#include <linux/module.h>
478c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
488c2ecf20Sopenharmony_ci#include <linux/usb.h>
498c2ecf20Sopenharmony_ci#include <linux/tty.h>
508c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
518c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
528c2ecf20Sopenharmony_ci#include <linux/kmod.h>
538c2ecf20Sopenharmony_ci#include <linux/rfkill.h>
548c2ecf20Sopenharmony_ci#include <linux/ip.h>
558c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
568c2ecf20Sopenharmony_ci#include <linux/usb/cdc.h>
578c2ecf20Sopenharmony_ci#include <net/arp.h>
588c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
598c2ecf20Sopenharmony_ci#include <linux/serial_core.h>
608c2ecf20Sopenharmony_ci#include <linux/serial.h>
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define MOD_AUTHOR			"Option Wireless"
648c2ecf20Sopenharmony_ci#define MOD_DESCRIPTION			"USB High Speed Option driver"
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define HSO_MAX_NET_DEVICES		10
678c2ecf20Sopenharmony_ci#define HSO__MAX_MTU			2048
688c2ecf20Sopenharmony_ci#define DEFAULT_MTU			1500
698c2ecf20Sopenharmony_ci#define DEFAULT_MRU			1500
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define CTRL_URB_RX_SIZE		1024
728c2ecf20Sopenharmony_ci#define CTRL_URB_TX_SIZE		64
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define BULK_URB_RX_SIZE		4096
758c2ecf20Sopenharmony_ci#define BULK_URB_TX_SIZE		8192
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define MUX_BULK_RX_BUF_SIZE		HSO__MAX_MTU
788c2ecf20Sopenharmony_ci#define MUX_BULK_TX_BUF_SIZE		HSO__MAX_MTU
798c2ecf20Sopenharmony_ci#define MUX_BULK_RX_BUF_COUNT		4
808c2ecf20Sopenharmony_ci#define USB_TYPE_OPTION_VENDOR		0x20
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* These definitions are used with the struct hso_net flags element */
838c2ecf20Sopenharmony_ci/* - use *_bit operations on it. (bit indices not values.) */
848c2ecf20Sopenharmony_ci#define HSO_NET_RUNNING			0
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define	HSO_NET_TX_TIMEOUT		(HZ*10)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define HSO_SERIAL_MAGIC		0x48534f31
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Number of ttys to handle */
918c2ecf20Sopenharmony_ci#define HSO_SERIAL_TTY_MINORS		256
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define MAX_RX_URBS			2
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*****************************************************************************/
968c2ecf20Sopenharmony_ci/* Debugging functions                                                       */
978c2ecf20Sopenharmony_ci/*****************************************************************************/
988c2ecf20Sopenharmony_ci#define hso_dbg(lvl, fmt, ...)						\
998c2ecf20Sopenharmony_cido {									\
1008c2ecf20Sopenharmony_ci	if ((lvl) & debug)						\
1018c2ecf20Sopenharmony_ci		pr_info("[%d:%s] " fmt,					\
1028c2ecf20Sopenharmony_ci			__LINE__, __func__, ##__VA_ARGS__);		\
1038c2ecf20Sopenharmony_ci} while (0)
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*****************************************************************************/
1068c2ecf20Sopenharmony_ci/* Enumerators                                                               */
1078c2ecf20Sopenharmony_ci/*****************************************************************************/
1088c2ecf20Sopenharmony_cienum pkt_parse_state {
1098c2ecf20Sopenharmony_ci	WAIT_IP,
1108c2ecf20Sopenharmony_ci	WAIT_DATA,
1118c2ecf20Sopenharmony_ci	WAIT_SYNC
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/*****************************************************************************/
1158c2ecf20Sopenharmony_ci/* Structs                                                                   */
1168c2ecf20Sopenharmony_ci/*****************************************************************************/
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct hso_shared_int {
1198c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *intr_endp;
1208c2ecf20Sopenharmony_ci	void *shared_intr_buf;
1218c2ecf20Sopenharmony_ci	struct urb *shared_intr_urb;
1228c2ecf20Sopenharmony_ci	struct usb_device *usb;
1238c2ecf20Sopenharmony_ci	int use_count;
1248c2ecf20Sopenharmony_ci	int ref_count;
1258c2ecf20Sopenharmony_ci	struct mutex shared_int_lock;
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistruct hso_net {
1298c2ecf20Sopenharmony_ci	struct hso_device *parent;
1308c2ecf20Sopenharmony_ci	struct net_device *net;
1318c2ecf20Sopenharmony_ci	struct rfkill *rfkill;
1328c2ecf20Sopenharmony_ci	char name[24];
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *in_endp;
1358c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *out_endp;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT];
1388c2ecf20Sopenharmony_ci	struct urb *mux_bulk_tx_urb;
1398c2ecf20Sopenharmony_ci	void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT];
1408c2ecf20Sopenharmony_ci	void *mux_bulk_tx_buf;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	struct sk_buff *skb_rx_buf;
1438c2ecf20Sopenharmony_ci	struct sk_buff *skb_tx_buf;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	enum pkt_parse_state rx_parse_state;
1468c2ecf20Sopenharmony_ci	spinlock_t net_lock;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	unsigned short rx_buf_size;
1498c2ecf20Sopenharmony_ci	unsigned short rx_buf_missing;
1508c2ecf20Sopenharmony_ci	struct iphdr rx_ip_hdr;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	unsigned long flags;
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cienum rx_ctrl_state{
1568c2ecf20Sopenharmony_ci	RX_IDLE,
1578c2ecf20Sopenharmony_ci	RX_SENT,
1588c2ecf20Sopenharmony_ci	RX_PENDING
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#define BM_REQUEST_TYPE (0xa1)
1628c2ecf20Sopenharmony_ci#define B_NOTIFICATION  (0x20)
1638c2ecf20Sopenharmony_ci#define W_VALUE         (0x0)
1648c2ecf20Sopenharmony_ci#define W_LENGTH        (0x2)
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define B_OVERRUN       (0x1<<6)
1678c2ecf20Sopenharmony_ci#define B_PARITY        (0x1<<5)
1688c2ecf20Sopenharmony_ci#define B_FRAMING       (0x1<<4)
1698c2ecf20Sopenharmony_ci#define B_RING_SIGNAL   (0x1<<3)
1708c2ecf20Sopenharmony_ci#define B_BREAK         (0x1<<2)
1718c2ecf20Sopenharmony_ci#define B_TX_CARRIER    (0x1<<1)
1728c2ecf20Sopenharmony_ci#define B_RX_CARRIER    (0x1<<0)
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistruct hso_serial_state_notification {
1758c2ecf20Sopenharmony_ci	u8 bmRequestType;
1768c2ecf20Sopenharmony_ci	u8 bNotification;
1778c2ecf20Sopenharmony_ci	u16 wValue;
1788c2ecf20Sopenharmony_ci	u16 wIndex;
1798c2ecf20Sopenharmony_ci	u16 wLength;
1808c2ecf20Sopenharmony_ci	u16 UART_state_bitmap;
1818c2ecf20Sopenharmony_ci} __packed;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistruct hso_tiocmget {
1848c2ecf20Sopenharmony_ci	struct mutex mutex;
1858c2ecf20Sopenharmony_ci	wait_queue_head_t waitq;
1868c2ecf20Sopenharmony_ci	int    intr_completed;
1878c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endp;
1888c2ecf20Sopenharmony_ci	struct urb *urb;
1898c2ecf20Sopenharmony_ci	struct hso_serial_state_notification *serial_state_notification;
1908c2ecf20Sopenharmony_ci	u16    prev_UART_state_bitmap;
1918c2ecf20Sopenharmony_ci	struct uart_icount icount;
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistruct hso_serial {
1968c2ecf20Sopenharmony_ci	struct hso_device *parent;
1978c2ecf20Sopenharmony_ci	int magic;
1988c2ecf20Sopenharmony_ci	u8 minor;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	struct hso_shared_int *shared_int;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* rx/tx urb could be either a bulk urb or a control urb depending
2038c2ecf20Sopenharmony_ci	   on which serial port it is used on. */
2048c2ecf20Sopenharmony_ci	struct urb *rx_urb[MAX_RX_URBS];
2058c2ecf20Sopenharmony_ci	u8 num_rx_urbs;
2068c2ecf20Sopenharmony_ci	u8 *rx_data[MAX_RX_URBS];
2078c2ecf20Sopenharmony_ci	u16 rx_data_length;	/* should contain allocated length */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	struct urb *tx_urb;
2108c2ecf20Sopenharmony_ci	u8 *tx_data;
2118c2ecf20Sopenharmony_ci	u8 *tx_buffer;
2128c2ecf20Sopenharmony_ci	u16 tx_data_length;	/* should contain allocated length */
2138c2ecf20Sopenharmony_ci	u16 tx_data_count;
2148c2ecf20Sopenharmony_ci	u16 tx_buffer_count;
2158c2ecf20Sopenharmony_ci	struct usb_ctrlrequest ctrl_req_tx;
2168c2ecf20Sopenharmony_ci	struct usb_ctrlrequest ctrl_req_rx;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *in_endp;
2198c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *out_endp;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	enum rx_ctrl_state rx_state;
2228c2ecf20Sopenharmony_ci	u8 rts_state;
2238c2ecf20Sopenharmony_ci	u8 dtr_state;
2248c2ecf20Sopenharmony_ci	unsigned tx_urb_used:1;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	struct tty_port port;
2278c2ecf20Sopenharmony_ci	/* from usb_serial_port */
2288c2ecf20Sopenharmony_ci	spinlock_t serial_lock;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	int (*write_data) (struct hso_serial *serial);
2318c2ecf20Sopenharmony_ci	struct hso_tiocmget  *tiocmget;
2328c2ecf20Sopenharmony_ci	/* Hacks required to get flow control
2338c2ecf20Sopenharmony_ci	 * working on the serial receive buffers
2348c2ecf20Sopenharmony_ci	 * so as not to drop characters on the floor.
2358c2ecf20Sopenharmony_ci	 */
2368c2ecf20Sopenharmony_ci	int  curr_rx_urb_idx;
2378c2ecf20Sopenharmony_ci	u8   rx_urb_filled[MAX_RX_URBS];
2388c2ecf20Sopenharmony_ci	struct tasklet_struct unthrottle_tasklet;
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistruct hso_device {
2428c2ecf20Sopenharmony_ci	union {
2438c2ecf20Sopenharmony_ci		struct hso_serial *dev_serial;
2448c2ecf20Sopenharmony_ci		struct hso_net *dev_net;
2458c2ecf20Sopenharmony_ci	} port_data;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	u32 port_spec;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	u8 is_active;
2508c2ecf20Sopenharmony_ci	u8 usb_gone;
2518c2ecf20Sopenharmony_ci	struct work_struct async_get_intf;
2528c2ecf20Sopenharmony_ci	struct work_struct async_put_intf;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	struct usb_device *usb;
2558c2ecf20Sopenharmony_ci	struct usb_interface *interface;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	struct device *dev;
2588c2ecf20Sopenharmony_ci	struct kref ref;
2598c2ecf20Sopenharmony_ci	struct mutex mutex;
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/* Type of interface */
2638c2ecf20Sopenharmony_ci#define HSO_INTF_MASK		0xFF00
2648c2ecf20Sopenharmony_ci#define	HSO_INTF_MUX		0x0100
2658c2ecf20Sopenharmony_ci#define	HSO_INTF_BULK   	0x0200
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/* Type of port */
2688c2ecf20Sopenharmony_ci#define HSO_PORT_MASK		0xFF
2698c2ecf20Sopenharmony_ci#define HSO_PORT_NO_PORT	0x0
2708c2ecf20Sopenharmony_ci#define	HSO_PORT_CONTROL	0x1
2718c2ecf20Sopenharmony_ci#define	HSO_PORT_APP		0x2
2728c2ecf20Sopenharmony_ci#define	HSO_PORT_GPS		0x3
2738c2ecf20Sopenharmony_ci#define	HSO_PORT_PCSC		0x4
2748c2ecf20Sopenharmony_ci#define	HSO_PORT_APP2		0x5
2758c2ecf20Sopenharmony_ci#define HSO_PORT_GPS_CONTROL	0x6
2768c2ecf20Sopenharmony_ci#define HSO_PORT_MSD		0x7
2778c2ecf20Sopenharmony_ci#define HSO_PORT_VOICE		0x8
2788c2ecf20Sopenharmony_ci#define HSO_PORT_DIAG2		0x9
2798c2ecf20Sopenharmony_ci#define	HSO_PORT_DIAG		0x10
2808c2ecf20Sopenharmony_ci#define	HSO_PORT_MODEM		0x11
2818c2ecf20Sopenharmony_ci#define	HSO_PORT_NETWORK	0x12
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/* Additional device info */
2848c2ecf20Sopenharmony_ci#define HSO_INFO_MASK		0xFF000000
2858c2ecf20Sopenharmony_ci#define HSO_INFO_CRC_BUG	0x01000000
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*****************************************************************************/
2888c2ecf20Sopenharmony_ci/* Prototypes                                                                */
2898c2ecf20Sopenharmony_ci/*****************************************************************************/
2908c2ecf20Sopenharmony_ci/* Serial driver functions */
2918c2ecf20Sopenharmony_cistatic int hso_serial_tiocmset(struct tty_struct *tty,
2928c2ecf20Sopenharmony_ci			       unsigned int set, unsigned int clear);
2938c2ecf20Sopenharmony_cistatic void ctrl_callback(struct urb *urb);
2948c2ecf20Sopenharmony_cistatic int put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
2958c2ecf20Sopenharmony_cistatic void hso_kick_transmit(struct hso_serial *serial);
2968c2ecf20Sopenharmony_ci/* Helper functions */
2978c2ecf20Sopenharmony_cistatic int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
2988c2ecf20Sopenharmony_ci				   struct usb_device *usb, gfp_t gfp);
2998c2ecf20Sopenharmony_cistatic void handle_usb_error(int status, const char *function,
3008c2ecf20Sopenharmony_ci			     struct hso_device *hso_dev);
3018c2ecf20Sopenharmony_cistatic struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
3028c2ecf20Sopenharmony_ci						  int type, int dir);
3038c2ecf20Sopenharmony_cistatic int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports);
3048c2ecf20Sopenharmony_cistatic void hso_free_interface(struct usb_interface *intf);
3058c2ecf20Sopenharmony_cistatic int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags);
3068c2ecf20Sopenharmony_cistatic int hso_stop_serial_device(struct hso_device *hso_dev);
3078c2ecf20Sopenharmony_cistatic int hso_start_net_device(struct hso_device *hso_dev);
3088c2ecf20Sopenharmony_cistatic void hso_free_shared_int(struct hso_shared_int *shared_int);
3098c2ecf20Sopenharmony_cistatic int hso_stop_net_device(struct hso_device *hso_dev);
3108c2ecf20Sopenharmony_cistatic void hso_serial_ref_free(struct kref *ref);
3118c2ecf20Sopenharmony_cistatic void hso_std_serial_read_bulk_callback(struct urb *urb);
3128c2ecf20Sopenharmony_cistatic int hso_mux_serial_read(struct hso_serial *serial);
3138c2ecf20Sopenharmony_cistatic void async_get_intf(struct work_struct *data);
3148c2ecf20Sopenharmony_cistatic void async_put_intf(struct work_struct *data);
3158c2ecf20Sopenharmony_cistatic int hso_put_activity(struct hso_device *hso_dev);
3168c2ecf20Sopenharmony_cistatic int hso_get_activity(struct hso_device *hso_dev);
3178c2ecf20Sopenharmony_cistatic void tiocmget_intr_callback(struct urb *urb);
3188c2ecf20Sopenharmony_ci/*****************************************************************************/
3198c2ecf20Sopenharmony_ci/* Helping functions                                                         */
3208c2ecf20Sopenharmony_ci/*****************************************************************************/
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/* #define DEBUG */
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic inline struct hso_net *dev2net(struct hso_device *hso_dev)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	return hso_dev->port_data.dev_net;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic inline struct hso_serial *dev2ser(struct hso_device *hso_dev)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	return hso_dev->port_data.dev_serial;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/* Debugging functions */
3358c2ecf20Sopenharmony_ci#ifdef DEBUG
3368c2ecf20Sopenharmony_cistatic void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
3378c2ecf20Sopenharmony_ci		     unsigned int len)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	static char name[255];
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	sprintf(name, "hso[%d:%s]", line_count, func_name);
3428c2ecf20Sopenharmony_ci	print_hex_dump_bytes(name, DUMP_PREFIX_NONE, buf, len);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci#define DUMP(buf_, len_)	\
3468c2ecf20Sopenharmony_ci	dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_)
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci#define DUMP1(buf_, len_)			\
3498c2ecf20Sopenharmony_ci	do {					\
3508c2ecf20Sopenharmony_ci		if (0x01 & debug)		\
3518c2ecf20Sopenharmony_ci			DUMP(buf_, len_);	\
3528c2ecf20Sopenharmony_ci	} while (0)
3538c2ecf20Sopenharmony_ci#else
3548c2ecf20Sopenharmony_ci#define DUMP(buf_, len_)
3558c2ecf20Sopenharmony_ci#define DUMP1(buf_, len_)
3568c2ecf20Sopenharmony_ci#endif
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* module parameters */
3598c2ecf20Sopenharmony_cistatic int debug;
3608c2ecf20Sopenharmony_cistatic int tty_major;
3618c2ecf20Sopenharmony_cistatic int disable_net;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/* driver info */
3648c2ecf20Sopenharmony_cistatic const char driver_name[] = "hso";
3658c2ecf20Sopenharmony_cistatic const char tty_filename[] = "ttyHS";
3668c2ecf20Sopenharmony_cistatic const char *version = __FILE__ ": " MOD_AUTHOR;
3678c2ecf20Sopenharmony_ci/* the usb driver itself (registered in hso_init) */
3688c2ecf20Sopenharmony_cistatic struct usb_driver hso_driver;
3698c2ecf20Sopenharmony_ci/* serial structures */
3708c2ecf20Sopenharmony_cistatic struct tty_driver *tty_drv;
3718c2ecf20Sopenharmony_cistatic struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
3728c2ecf20Sopenharmony_cistatic struct hso_device *network_table[HSO_MAX_NET_DEVICES];
3738c2ecf20Sopenharmony_cistatic spinlock_t serial_table_lock;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic const s32 default_port_spec[] = {
3768c2ecf20Sopenharmony_ci	HSO_INTF_MUX | HSO_PORT_NETWORK,
3778c2ecf20Sopenharmony_ci	HSO_INTF_BULK | HSO_PORT_DIAG,
3788c2ecf20Sopenharmony_ci	HSO_INTF_BULK | HSO_PORT_MODEM,
3798c2ecf20Sopenharmony_ci	0
3808c2ecf20Sopenharmony_ci};
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic const s32 icon321_port_spec[] = {
3838c2ecf20Sopenharmony_ci	HSO_INTF_MUX | HSO_PORT_NETWORK,
3848c2ecf20Sopenharmony_ci	HSO_INTF_BULK | HSO_PORT_DIAG2,
3858c2ecf20Sopenharmony_ci	HSO_INTF_BULK | HSO_PORT_MODEM,
3868c2ecf20Sopenharmony_ci	HSO_INTF_BULK | HSO_PORT_DIAG,
3878c2ecf20Sopenharmony_ci	0
3888c2ecf20Sopenharmony_ci};
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci#define default_port_device(vendor, product)	\
3918c2ecf20Sopenharmony_ci	USB_DEVICE(vendor, product),	\
3928c2ecf20Sopenharmony_ci		.driver_info = (kernel_ulong_t)default_port_spec
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci#define icon321_port_device(vendor, product)	\
3958c2ecf20Sopenharmony_ci	USB_DEVICE(vendor, product),	\
3968c2ecf20Sopenharmony_ci		.driver_info = (kernel_ulong_t)icon321_port_spec
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/* list of devices we support */
3998c2ecf20Sopenharmony_cistatic const struct usb_device_id hso_ids[] = {
4008c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6711)},
4018c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6731)},
4028c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6751)},
4038c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6771)},
4048c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6791)},
4058c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6811)},
4068c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6911)},
4078c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6951)},
4088c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x6971)},
4098c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7011)},
4108c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7031)},
4118c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7051)},
4128c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7071)},
4138c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7111)},
4148c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7211)},
4158c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7251)},
4168c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7271)},
4178c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0x7311)},
4188c2ecf20Sopenharmony_ci	{default_port_device(0x0af0, 0xc031)},	/* Icon-Edge */
4198c2ecf20Sopenharmony_ci	{icon321_port_device(0x0af0, 0xd013)},	/* Module HSxPA */
4208c2ecf20Sopenharmony_ci	{icon321_port_device(0x0af0, 0xd031)},	/* Icon-321 */
4218c2ecf20Sopenharmony_ci	{icon321_port_device(0x0af0, 0xd033)},	/* Icon-322 */
4228c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7301)},		/* GE40x */
4238c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7361)},		/* GE40x */
4248c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7381)},		/* GE40x */
4258c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7401)},		/* GI 0401 */
4268c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7501)},		/* GTM 382 */
4278c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7601)},		/* GE40x */
4288c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7701)},
4298c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7706)},
4308c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7801)},
4318c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7901)},
4328c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7A01)},
4338c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x7A05)},
4348c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8200)},
4358c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8201)},
4368c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8300)},
4378c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8302)},
4388c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8304)},
4398c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8400)},
4408c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8600)},
4418c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8800)},
4428c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x8900)},
4438c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x9000)},
4448c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0x9200)},		/* Option GTM671WFS */
4458c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd035)},
4468c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd055)},
4478c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd155)},
4488c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd255)},
4498c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd057)},
4508c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd157)},
4518c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd257)},
4528c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd357)},
4538c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xd058)},
4548c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0af0, 0xc100)},
4558c2ecf20Sopenharmony_ci	{}
4568c2ecf20Sopenharmony_ci};
4578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, hso_ids);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/* Sysfs attribute */
4608c2ecf20Sopenharmony_cistatic ssize_t hso_sysfs_show_porttype(struct device *dev,
4618c2ecf20Sopenharmony_ci				       struct device_attribute *attr,
4628c2ecf20Sopenharmony_ci				       char *buf)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct hso_device *hso_dev = dev_get_drvdata(dev);
4658c2ecf20Sopenharmony_ci	char *port_name;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (!hso_dev)
4688c2ecf20Sopenharmony_ci		return 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	switch (hso_dev->port_spec & HSO_PORT_MASK) {
4718c2ecf20Sopenharmony_ci	case HSO_PORT_CONTROL:
4728c2ecf20Sopenharmony_ci		port_name = "Control";
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci	case HSO_PORT_APP:
4758c2ecf20Sopenharmony_ci		port_name = "Application";
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci	case HSO_PORT_APP2:
4788c2ecf20Sopenharmony_ci		port_name = "Application2";
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case HSO_PORT_GPS:
4818c2ecf20Sopenharmony_ci		port_name = "GPS";
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci	case HSO_PORT_GPS_CONTROL:
4848c2ecf20Sopenharmony_ci		port_name = "GPS Control";
4858c2ecf20Sopenharmony_ci		break;
4868c2ecf20Sopenharmony_ci	case HSO_PORT_PCSC:
4878c2ecf20Sopenharmony_ci		port_name = "PCSC";
4888c2ecf20Sopenharmony_ci		break;
4898c2ecf20Sopenharmony_ci	case HSO_PORT_DIAG:
4908c2ecf20Sopenharmony_ci		port_name = "Diagnostic";
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	case HSO_PORT_DIAG2:
4938c2ecf20Sopenharmony_ci		port_name = "Diagnostic2";
4948c2ecf20Sopenharmony_ci		break;
4958c2ecf20Sopenharmony_ci	case HSO_PORT_MODEM:
4968c2ecf20Sopenharmony_ci		port_name = "Modem";
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci	case HSO_PORT_NETWORK:
4998c2ecf20Sopenharmony_ci		port_name = "Network";
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci	default:
5028c2ecf20Sopenharmony_ci		port_name = "Unknown";
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", port_name);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(hsotype, 0444, hso_sysfs_show_porttype, NULL);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic struct attribute *hso_serial_dev_attrs[] = {
5118c2ecf20Sopenharmony_ci	&dev_attr_hsotype.attr,
5128c2ecf20Sopenharmony_ci	NULL
5138c2ecf20Sopenharmony_ci};
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(hso_serial_dev);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int hso_urb_to_index(struct hso_serial *serial, struct urb *urb)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	int idx;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	for (idx = 0; idx < serial->num_rx_urbs; idx++)
5228c2ecf20Sopenharmony_ci		if (serial->rx_urb[idx] == urb)
5238c2ecf20Sopenharmony_ci			return idx;
5248c2ecf20Sopenharmony_ci	dev_err(serial->parent->dev, "hso_urb_to_index failed\n");
5258c2ecf20Sopenharmony_ci	return -1;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/* converts mux value to a port spec value */
5298c2ecf20Sopenharmony_cistatic u32 hso_mux_to_port(int mux)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	u32 result;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	switch (mux) {
5348c2ecf20Sopenharmony_ci	case 0x1:
5358c2ecf20Sopenharmony_ci		result = HSO_PORT_CONTROL;
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci	case 0x2:
5388c2ecf20Sopenharmony_ci		result = HSO_PORT_APP;
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci	case 0x4:
5418c2ecf20Sopenharmony_ci		result = HSO_PORT_PCSC;
5428c2ecf20Sopenharmony_ci		break;
5438c2ecf20Sopenharmony_ci	case 0x8:
5448c2ecf20Sopenharmony_ci		result = HSO_PORT_GPS;
5458c2ecf20Sopenharmony_ci		break;
5468c2ecf20Sopenharmony_ci	case 0x10:
5478c2ecf20Sopenharmony_ci		result = HSO_PORT_APP2;
5488c2ecf20Sopenharmony_ci		break;
5498c2ecf20Sopenharmony_ci	default:
5508c2ecf20Sopenharmony_ci		result = HSO_PORT_NO_PORT;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	return result;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci/* converts port spec value to a mux value */
5568c2ecf20Sopenharmony_cistatic u32 hso_port_to_mux(int port)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	u32 result;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	switch (port & HSO_PORT_MASK) {
5618c2ecf20Sopenharmony_ci	case HSO_PORT_CONTROL:
5628c2ecf20Sopenharmony_ci		result = 0x0;
5638c2ecf20Sopenharmony_ci		break;
5648c2ecf20Sopenharmony_ci	case HSO_PORT_APP:
5658c2ecf20Sopenharmony_ci		result = 0x1;
5668c2ecf20Sopenharmony_ci		break;
5678c2ecf20Sopenharmony_ci	case HSO_PORT_PCSC:
5688c2ecf20Sopenharmony_ci		result = 0x2;
5698c2ecf20Sopenharmony_ci		break;
5708c2ecf20Sopenharmony_ci	case HSO_PORT_GPS:
5718c2ecf20Sopenharmony_ci		result = 0x3;
5728c2ecf20Sopenharmony_ci		break;
5738c2ecf20Sopenharmony_ci	case HSO_PORT_APP2:
5748c2ecf20Sopenharmony_ci		result = 0x4;
5758c2ecf20Sopenharmony_ci		break;
5768c2ecf20Sopenharmony_ci	default:
5778c2ecf20Sopenharmony_ci		result = 0x0;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci	return result;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic struct hso_serial *get_serial_by_shared_int_and_type(
5838c2ecf20Sopenharmony_ci					struct hso_shared_int *shared_int,
5848c2ecf20Sopenharmony_ci					int mux)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	int i, port;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	port = hso_mux_to_port(mux);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
5918c2ecf20Sopenharmony_ci		if (serial_table[i] &&
5928c2ecf20Sopenharmony_ci		    (dev2ser(serial_table[i])->shared_int == shared_int) &&
5938c2ecf20Sopenharmony_ci		    ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
5948c2ecf20Sopenharmony_ci			return dev2ser(serial_table[i]);
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return NULL;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic struct hso_serial *get_serial_by_index(unsigned index)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct hso_serial *serial = NULL;
6048c2ecf20Sopenharmony_ci	unsigned long flags;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial_table_lock, flags);
6078c2ecf20Sopenharmony_ci	if (serial_table[index])
6088c2ecf20Sopenharmony_ci		serial = dev2ser(serial_table[index]);
6098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial_table_lock, flags);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return serial;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int obtain_minor(struct hso_serial *serial)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	int index;
6178c2ecf20Sopenharmony_ci	unsigned long flags;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial_table_lock, flags);
6208c2ecf20Sopenharmony_ci	for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) {
6218c2ecf20Sopenharmony_ci		if (serial_table[index] == NULL) {
6228c2ecf20Sopenharmony_ci			serial_table[index] = serial->parent;
6238c2ecf20Sopenharmony_ci			serial->minor = index;
6248c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&serial_table_lock, flags);
6258c2ecf20Sopenharmony_ci			return 0;
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial_table_lock, flags);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	pr_err("%s: no free serial devices in table\n", __func__);
6318c2ecf20Sopenharmony_ci	return -1;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic void release_minor(struct hso_serial *serial)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	unsigned long flags;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial_table_lock, flags);
6398c2ecf20Sopenharmony_ci	serial_table[serial->minor] = NULL;
6408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial_table_lock, flags);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic void handle_usb_error(int status, const char *function,
6448c2ecf20Sopenharmony_ci			     struct hso_device *hso_dev)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	char *explanation;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	switch (status) {
6498c2ecf20Sopenharmony_ci	case -ENODEV:
6508c2ecf20Sopenharmony_ci		explanation = "no device";
6518c2ecf20Sopenharmony_ci		break;
6528c2ecf20Sopenharmony_ci	case -ENOENT:
6538c2ecf20Sopenharmony_ci		explanation = "endpoint not enabled";
6548c2ecf20Sopenharmony_ci		break;
6558c2ecf20Sopenharmony_ci	case -EPIPE:
6568c2ecf20Sopenharmony_ci		explanation = "endpoint stalled";
6578c2ecf20Sopenharmony_ci		break;
6588c2ecf20Sopenharmony_ci	case -ENOSPC:
6598c2ecf20Sopenharmony_ci		explanation = "not enough bandwidth";
6608c2ecf20Sopenharmony_ci		break;
6618c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
6628c2ecf20Sopenharmony_ci		explanation = "device disabled";
6638c2ecf20Sopenharmony_ci		break;
6648c2ecf20Sopenharmony_ci	case -EHOSTUNREACH:
6658c2ecf20Sopenharmony_ci		explanation = "device suspended";
6668c2ecf20Sopenharmony_ci		break;
6678c2ecf20Sopenharmony_ci	case -EINVAL:
6688c2ecf20Sopenharmony_ci	case -EAGAIN:
6698c2ecf20Sopenharmony_ci	case -EFBIG:
6708c2ecf20Sopenharmony_ci	case -EMSGSIZE:
6718c2ecf20Sopenharmony_ci		explanation = "internal error";
6728c2ecf20Sopenharmony_ci		break;
6738c2ecf20Sopenharmony_ci	case -EILSEQ:
6748c2ecf20Sopenharmony_ci	case -EPROTO:
6758c2ecf20Sopenharmony_ci	case -ETIME:
6768c2ecf20Sopenharmony_ci	case -ETIMEDOUT:
6778c2ecf20Sopenharmony_ci		explanation = "protocol error";
6788c2ecf20Sopenharmony_ci		if (hso_dev)
6798c2ecf20Sopenharmony_ci			usb_queue_reset_device(hso_dev->interface);
6808c2ecf20Sopenharmony_ci		break;
6818c2ecf20Sopenharmony_ci	default:
6828c2ecf20Sopenharmony_ci		explanation = "unknown status";
6838c2ecf20Sopenharmony_ci		break;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* log a meaningful explanation of an USB status */
6878c2ecf20Sopenharmony_ci	hso_dbg(0x1, "%s: received USB status - %s (%d)\n",
6888c2ecf20Sopenharmony_ci		function, explanation, status);
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci/* Network interface functions */
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci/* called when net interface is brought up by ifconfig */
6948c2ecf20Sopenharmony_cistatic int hso_net_open(struct net_device *net)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct hso_net *odev = netdev_priv(net);
6978c2ecf20Sopenharmony_ci	unsigned long flags = 0;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (!odev) {
7008c2ecf20Sopenharmony_ci		dev_err(&net->dev, "No net device !\n");
7018c2ecf20Sopenharmony_ci		return -ENODEV;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	odev->skb_tx_buf = NULL;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* setup environment */
7078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&odev->net_lock, flags);
7088c2ecf20Sopenharmony_ci	odev->rx_parse_state = WAIT_IP;
7098c2ecf20Sopenharmony_ci	odev->rx_buf_size = 0;
7108c2ecf20Sopenharmony_ci	odev->rx_buf_missing = sizeof(struct iphdr);
7118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&odev->net_lock, flags);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	/* We are up and running. */
7148c2ecf20Sopenharmony_ci	set_bit(HSO_NET_RUNNING, &odev->flags);
7158c2ecf20Sopenharmony_ci	hso_start_net_device(odev->parent);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Tell the kernel we are ready to start receiving from it */
7188c2ecf20Sopenharmony_ci	netif_start_queue(net);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return 0;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/* called when interface is brought down by ifconfig */
7248c2ecf20Sopenharmony_cistatic int hso_net_close(struct net_device *net)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct hso_net *odev = netdev_priv(net);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* we don't need the queue anymore */
7298c2ecf20Sopenharmony_ci	netif_stop_queue(net);
7308c2ecf20Sopenharmony_ci	/* no longer running */
7318c2ecf20Sopenharmony_ci	clear_bit(HSO_NET_RUNNING, &odev->flags);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	hso_stop_net_device(odev->parent);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/* done */
7368c2ecf20Sopenharmony_ci	return 0;
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci/* USB tells is xmit done, we should start the netqueue again */
7408c2ecf20Sopenharmony_cistatic void write_bulk_callback(struct urb *urb)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct hso_net *odev = urb->context;
7438c2ecf20Sopenharmony_ci	int status = urb->status;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/* Sanity check */
7468c2ecf20Sopenharmony_ci	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
7478c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev, "%s: device not running\n", __func__);
7488c2ecf20Sopenharmony_ci		return;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* Do we still have a valid kernel network device? */
7528c2ecf20Sopenharmony_ci	if (!netif_device_present(odev->net)) {
7538c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev, "%s: net device not present\n",
7548c2ecf20Sopenharmony_ci			__func__);
7558c2ecf20Sopenharmony_ci		return;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/* log status, but don't act on it, we don't need to resubmit anything
7598c2ecf20Sopenharmony_ci	 * anyhow */
7608c2ecf20Sopenharmony_ci	if (status)
7618c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, odev->parent);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	hso_put_activity(odev->parent);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* Tell the network interface we are ready for another frame */
7668c2ecf20Sopenharmony_ci	netif_wake_queue(odev->net);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci/* called by kernel when we need to transmit a packet */
7708c2ecf20Sopenharmony_cistatic netdev_tx_t hso_net_start_xmit(struct sk_buff *skb,
7718c2ecf20Sopenharmony_ci					    struct net_device *net)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct hso_net *odev = netdev_priv(net);
7748c2ecf20Sopenharmony_ci	int result;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Tell the kernel, "No more frames 'til we are done with this one." */
7778c2ecf20Sopenharmony_ci	netif_stop_queue(net);
7788c2ecf20Sopenharmony_ci	if (hso_get_activity(odev->parent) == -EAGAIN) {
7798c2ecf20Sopenharmony_ci		odev->skb_tx_buf = skb;
7808c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* log if asked */
7848c2ecf20Sopenharmony_ci	DUMP1(skb->data, skb->len);
7858c2ecf20Sopenharmony_ci	/* Copy it from kernel memory to OUR memory */
7868c2ecf20Sopenharmony_ci	memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len);
7878c2ecf20Sopenharmony_ci	hso_dbg(0x1, "len: %d/%d\n", skb->len, MUX_BULK_TX_BUF_SIZE);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* Fill in the URB for shipping it out. */
7908c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(odev->mux_bulk_tx_urb,
7918c2ecf20Sopenharmony_ci			  odev->parent->usb,
7928c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(odev->parent->usb,
7938c2ecf20Sopenharmony_ci					  odev->out_endp->
7948c2ecf20Sopenharmony_ci					  bEndpointAddress & 0x7F),
7958c2ecf20Sopenharmony_ci			  odev->mux_bulk_tx_buf, skb->len, write_bulk_callback,
7968c2ecf20Sopenharmony_ci			  odev);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* Deal with the Zero Length packet problem, I hope */
7998c2ecf20Sopenharmony_ci	odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* Send the URB on its merry way. */
8028c2ecf20Sopenharmony_ci	result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC);
8038c2ecf20Sopenharmony_ci	if (result) {
8048c2ecf20Sopenharmony_ci		dev_warn(&odev->parent->interface->dev,
8058c2ecf20Sopenharmony_ci			"failed mux_bulk_tx_urb %d\n", result);
8068c2ecf20Sopenharmony_ci		net->stats.tx_errors++;
8078c2ecf20Sopenharmony_ci		netif_start_queue(net);
8088c2ecf20Sopenharmony_ci	} else {
8098c2ecf20Sopenharmony_ci		net->stats.tx_packets++;
8108c2ecf20Sopenharmony_ci		net->stats.tx_bytes += skb->len;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
8138c2ecf20Sopenharmony_ci	/* we're done */
8148c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic const struct ethtool_ops ops = {
8188c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link
8198c2ecf20Sopenharmony_ci};
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci/* called when a packet did not ack after watchdogtimeout */
8228c2ecf20Sopenharmony_cistatic void hso_net_tx_timeout(struct net_device *net, unsigned int txqueue)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct hso_net *odev = netdev_priv(net);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (!odev)
8278c2ecf20Sopenharmony_ci		return;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* Tell syslog we are hosed. */
8308c2ecf20Sopenharmony_ci	dev_warn(&net->dev, "Tx timed out.\n");
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	/* Tear the waiting frame off the list */
8338c2ecf20Sopenharmony_ci	if (odev->mux_bulk_tx_urb)
8348c2ecf20Sopenharmony_ci		usb_unlink_urb(odev->mux_bulk_tx_urb);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	/* Update statistics */
8378c2ecf20Sopenharmony_ci	net->stats.tx_errors++;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci/* make a real packet from the received USB buffer */
8418c2ecf20Sopenharmony_cistatic void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
8428c2ecf20Sopenharmony_ci			unsigned int count, unsigned char is_eop)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	unsigned short temp_bytes;
8458c2ecf20Sopenharmony_ci	unsigned short buffer_offset = 0;
8468c2ecf20Sopenharmony_ci	unsigned short frame_len;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/* log if needed */
8498c2ecf20Sopenharmony_ci	hso_dbg(0x1, "Rx %d bytes\n", count);
8508c2ecf20Sopenharmony_ci	DUMP(ip_pkt, min(128, (int)count));
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	while (count) {
8538c2ecf20Sopenharmony_ci		switch (odev->rx_parse_state) {
8548c2ecf20Sopenharmony_ci		case WAIT_IP:
8558c2ecf20Sopenharmony_ci			/* waiting for IP header. */
8568c2ecf20Sopenharmony_ci			/* wanted bytes - size of ip header */
8578c2ecf20Sopenharmony_ci			temp_bytes =
8588c2ecf20Sopenharmony_ci			    (count <
8598c2ecf20Sopenharmony_ci			     odev->rx_buf_missing) ? count : odev->
8608c2ecf20Sopenharmony_ci			    rx_buf_missing;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci			memcpy(((unsigned char *)(&odev->rx_ip_hdr)) +
8638c2ecf20Sopenharmony_ci			       odev->rx_buf_size, ip_pkt + buffer_offset,
8648c2ecf20Sopenharmony_ci			       temp_bytes);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci			odev->rx_buf_size += temp_bytes;
8678c2ecf20Sopenharmony_ci			buffer_offset += temp_bytes;
8688c2ecf20Sopenharmony_ci			odev->rx_buf_missing -= temp_bytes;
8698c2ecf20Sopenharmony_ci			count -= temp_bytes;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci			if (!odev->rx_buf_missing) {
8728c2ecf20Sopenharmony_ci				/* header is complete allocate an sk_buffer and
8738c2ecf20Sopenharmony_ci				 * continue to WAIT_DATA */
8748c2ecf20Sopenharmony_ci				frame_len = ntohs(odev->rx_ip_hdr.tot_len);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci				if ((frame_len > DEFAULT_MRU) ||
8778c2ecf20Sopenharmony_ci				    (frame_len < sizeof(struct iphdr))) {
8788c2ecf20Sopenharmony_ci					dev_err(&odev->net->dev,
8798c2ecf20Sopenharmony_ci						"Invalid frame (%d) length\n",
8808c2ecf20Sopenharmony_ci						frame_len);
8818c2ecf20Sopenharmony_ci					odev->rx_parse_state = WAIT_SYNC;
8828c2ecf20Sopenharmony_ci					continue;
8838c2ecf20Sopenharmony_ci				}
8848c2ecf20Sopenharmony_ci				/* Allocate an sk_buff */
8858c2ecf20Sopenharmony_ci				odev->skb_rx_buf = netdev_alloc_skb(odev->net,
8868c2ecf20Sopenharmony_ci								    frame_len);
8878c2ecf20Sopenharmony_ci				if (!odev->skb_rx_buf) {
8888c2ecf20Sopenharmony_ci					/* We got no receive buffer. */
8898c2ecf20Sopenharmony_ci					hso_dbg(0x1, "could not allocate memory\n");
8908c2ecf20Sopenharmony_ci					odev->rx_parse_state = WAIT_SYNC;
8918c2ecf20Sopenharmony_ci					continue;
8928c2ecf20Sopenharmony_ci				}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci				/* Copy what we got so far. make room for iphdr
8958c2ecf20Sopenharmony_ci				 * after tail. */
8968c2ecf20Sopenharmony_ci				skb_put_data(odev->skb_rx_buf,
8978c2ecf20Sopenharmony_ci					     (char *)&(odev->rx_ip_hdr),
8988c2ecf20Sopenharmony_ci					     sizeof(struct iphdr));
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci				/* ETH_HLEN */
9018c2ecf20Sopenharmony_ci				odev->rx_buf_size = sizeof(struct iphdr);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci				/* Filip actually use .tot_len */
9048c2ecf20Sopenharmony_ci				odev->rx_buf_missing =
9058c2ecf20Sopenharmony_ci				    frame_len - sizeof(struct iphdr);
9068c2ecf20Sopenharmony_ci				odev->rx_parse_state = WAIT_DATA;
9078c2ecf20Sopenharmony_ci			}
9088c2ecf20Sopenharmony_ci			break;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		case WAIT_DATA:
9118c2ecf20Sopenharmony_ci			temp_bytes = (count < odev->rx_buf_missing)
9128c2ecf20Sopenharmony_ci					? count : odev->rx_buf_missing;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci			/* Copy the rest of the bytes that are left in the
9158c2ecf20Sopenharmony_ci			 * buffer into the waiting sk_buf. */
9168c2ecf20Sopenharmony_ci			/* Make room for temp_bytes after tail. */
9178c2ecf20Sopenharmony_ci			skb_put_data(odev->skb_rx_buf,
9188c2ecf20Sopenharmony_ci				     ip_pkt + buffer_offset,
9198c2ecf20Sopenharmony_ci				     temp_bytes);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci			odev->rx_buf_missing -= temp_bytes;
9228c2ecf20Sopenharmony_ci			count -= temp_bytes;
9238c2ecf20Sopenharmony_ci			buffer_offset += temp_bytes;
9248c2ecf20Sopenharmony_ci			odev->rx_buf_size += temp_bytes;
9258c2ecf20Sopenharmony_ci			if (!odev->rx_buf_missing) {
9268c2ecf20Sopenharmony_ci				/* Packet is complete. Inject into stack. */
9278c2ecf20Sopenharmony_ci				/* We have IP packet here */
9288c2ecf20Sopenharmony_ci				odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP);
9298c2ecf20Sopenharmony_ci				skb_reset_mac_header(odev->skb_rx_buf);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci				/* Ship it off to the kernel */
9328c2ecf20Sopenharmony_ci				netif_rx(odev->skb_rx_buf);
9338c2ecf20Sopenharmony_ci				/* No longer our buffer. */
9348c2ecf20Sopenharmony_ci				odev->skb_rx_buf = NULL;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci				/* update out statistics */
9378c2ecf20Sopenharmony_ci				odev->net->stats.rx_packets++;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci				odev->net->stats.rx_bytes += odev->rx_buf_size;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci				odev->rx_buf_size = 0;
9428c2ecf20Sopenharmony_ci				odev->rx_buf_missing = sizeof(struct iphdr);
9438c2ecf20Sopenharmony_ci				odev->rx_parse_state = WAIT_IP;
9448c2ecf20Sopenharmony_ci			}
9458c2ecf20Sopenharmony_ci			break;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci		case WAIT_SYNC:
9488c2ecf20Sopenharmony_ci			hso_dbg(0x1, " W_S\n");
9498c2ecf20Sopenharmony_ci			count = 0;
9508c2ecf20Sopenharmony_ci			break;
9518c2ecf20Sopenharmony_ci		default:
9528c2ecf20Sopenharmony_ci			hso_dbg(0x1, "\n");
9538c2ecf20Sopenharmony_ci			count--;
9548c2ecf20Sopenharmony_ci			break;
9558c2ecf20Sopenharmony_ci		}
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	/* Recovery mechanism for WAIT_SYNC state. */
9598c2ecf20Sopenharmony_ci	if (is_eop) {
9608c2ecf20Sopenharmony_ci		if (odev->rx_parse_state == WAIT_SYNC) {
9618c2ecf20Sopenharmony_ci			odev->rx_parse_state = WAIT_IP;
9628c2ecf20Sopenharmony_ci			odev->rx_buf_size = 0;
9638c2ecf20Sopenharmony_ci			odev->rx_buf_missing = sizeof(struct iphdr);
9648c2ecf20Sopenharmony_ci		}
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic void fix_crc_bug(struct urb *urb, __le16 max_packet_size)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	static const u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
9718c2ecf20Sopenharmony_ci	u32 rest = urb->actual_length % le16_to_cpu(max_packet_size);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (((rest == 5) || (rest == 6)) &&
9748c2ecf20Sopenharmony_ci	    !memcmp(((u8 *)urb->transfer_buffer) + urb->actual_length - 4,
9758c2ecf20Sopenharmony_ci		    crc_check, 4)) {
9768c2ecf20Sopenharmony_ci		urb->actual_length -= 4;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci/* Moving data from usb to kernel (in interrupt state) */
9818c2ecf20Sopenharmony_cistatic void read_bulk_callback(struct urb *urb)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	struct hso_net *odev = urb->context;
9848c2ecf20Sopenharmony_ci	struct net_device *net;
9858c2ecf20Sopenharmony_ci	int result;
9868c2ecf20Sopenharmony_ci	unsigned long flags;
9878c2ecf20Sopenharmony_ci	int status = urb->status;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	/* is al ok?  (Filip: Who's Al ?) */
9908c2ecf20Sopenharmony_ci	if (status) {
9918c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, odev->parent);
9928c2ecf20Sopenharmony_ci		return;
9938c2ecf20Sopenharmony_ci	}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	/* Sanity check */
9968c2ecf20Sopenharmony_ci	if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
9978c2ecf20Sopenharmony_ci		hso_dbg(0x1, "BULK IN callback but driver is not active!\n");
9988c2ecf20Sopenharmony_ci		return;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci	usb_mark_last_busy(urb->dev);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	net = odev->net;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (!netif_device_present(net)) {
10058c2ecf20Sopenharmony_ci		/* Somebody killed our network interface... */
10068c2ecf20Sopenharmony_ci		return;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (odev->parent->port_spec & HSO_INFO_CRC_BUG)
10108c2ecf20Sopenharmony_ci		fix_crc_bug(urb, odev->in_endp->wMaxPacketSize);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* do we even have a packet? */
10138c2ecf20Sopenharmony_ci	if (urb->actual_length) {
10148c2ecf20Sopenharmony_ci		/* Handle the IP stream, add header and push it onto network
10158c2ecf20Sopenharmony_ci		 * stack if the packet is complete. */
10168c2ecf20Sopenharmony_ci		spin_lock_irqsave(&odev->net_lock, flags);
10178c2ecf20Sopenharmony_ci		packetizeRx(odev, urb->transfer_buffer, urb->actual_length,
10188c2ecf20Sopenharmony_ci			    (urb->transfer_buffer_length >
10198c2ecf20Sopenharmony_ci			     urb->actual_length) ? 1 : 0);
10208c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&odev->net_lock, flags);
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	/* We are done with this URB, resubmit it. Prep the USB to wait for
10248c2ecf20Sopenharmony_ci	 * another frame. Reuse same as received. */
10258c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb,
10268c2ecf20Sopenharmony_ci			  odev->parent->usb,
10278c2ecf20Sopenharmony_ci			  usb_rcvbulkpipe(odev->parent->usb,
10288c2ecf20Sopenharmony_ci					  odev->in_endp->
10298c2ecf20Sopenharmony_ci					  bEndpointAddress & 0x7F),
10308c2ecf20Sopenharmony_ci			  urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE,
10318c2ecf20Sopenharmony_ci			  read_bulk_callback, odev);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Give this to the USB subsystem so it can tell us when more data
10348c2ecf20Sopenharmony_ci	 * arrives. */
10358c2ecf20Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
10368c2ecf20Sopenharmony_ci	if (result)
10378c2ecf20Sopenharmony_ci		dev_warn(&odev->parent->interface->dev,
10388c2ecf20Sopenharmony_ci			 "%s failed submit mux_bulk_rx_urb %d\n", __func__,
10398c2ecf20Sopenharmony_ci			 result);
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci/* Serial driver functions */
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void hso_init_termios(struct ktermios *termios)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	/*
10478c2ecf20Sopenharmony_ci	 * The default requirements for this device are:
10488c2ecf20Sopenharmony_ci	 */
10498c2ecf20Sopenharmony_ci	termios->c_iflag &=
10508c2ecf20Sopenharmony_ci		~(IGNBRK	/* disable ignore break */
10518c2ecf20Sopenharmony_ci		| BRKINT	/* disable break causes interrupt */
10528c2ecf20Sopenharmony_ci		| PARMRK	/* disable mark parity errors */
10538c2ecf20Sopenharmony_ci		| ISTRIP	/* disable clear high bit of input characters */
10548c2ecf20Sopenharmony_ci		| INLCR		/* disable translate NL to CR */
10558c2ecf20Sopenharmony_ci		| IGNCR		/* disable ignore CR */
10568c2ecf20Sopenharmony_ci		| ICRNL		/* disable translate CR to NL */
10578c2ecf20Sopenharmony_ci		| IXON);	/* disable enable XON/XOFF flow control */
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	/* disable postprocess output characters */
10608c2ecf20Sopenharmony_ci	termios->c_oflag &= ~OPOST;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	termios->c_lflag &=
10638c2ecf20Sopenharmony_ci		~(ECHO		/* disable echo input characters */
10648c2ecf20Sopenharmony_ci		| ECHONL	/* disable echo new line */
10658c2ecf20Sopenharmony_ci		| ICANON	/* disable erase, kill, werase, and rprnt
10668c2ecf20Sopenharmony_ci				   special characters */
10678c2ecf20Sopenharmony_ci		| ISIG		/* disable interrupt, quit, and suspend special
10688c2ecf20Sopenharmony_ci				   characters */
10698c2ecf20Sopenharmony_ci		| IEXTEN);	/* disable non-POSIX special characters */
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	termios->c_cflag &=
10728c2ecf20Sopenharmony_ci		~(CSIZE		/* no size */
10738c2ecf20Sopenharmony_ci		| PARENB	/* disable parity bit */
10748c2ecf20Sopenharmony_ci		| CBAUD		/* clear current baud rate */
10758c2ecf20Sopenharmony_ci		| CBAUDEX);	/* clear current buad rate */
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	termios->c_cflag |= CS8;	/* character size 8 bits */
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/* baud rate 115200 */
10808c2ecf20Sopenharmony_ci	tty_termios_encode_baud_rate(termios, 115200, 115200);
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic void _hso_serial_set_termios(struct tty_struct *tty,
10848c2ecf20Sopenharmony_ci				    struct ktermios *old)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if (!serial) {
10898c2ecf20Sopenharmony_ci		pr_err("%s: no tty structures", __func__);
10908c2ecf20Sopenharmony_ci		return;
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	hso_dbg(0x8, "port %d\n", serial->minor);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/*
10968c2ecf20Sopenharmony_ci	 *	Fix up unsupported bits
10978c2ecf20Sopenharmony_ci	 */
10988c2ecf20Sopenharmony_ci	tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	tty->termios.c_cflag &=
11018c2ecf20Sopenharmony_ci		~(CSIZE		/* no size */
11028c2ecf20Sopenharmony_ci		| PARENB	/* disable parity bit */
11038c2ecf20Sopenharmony_ci		| CBAUD		/* clear current baud rate */
11048c2ecf20Sopenharmony_ci		| CBAUDEX);	/* clear current buad rate */
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	tty->termios.c_cflag |= CS8;	/* character size 8 bits */
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	/* baud rate 115200 */
11098c2ecf20Sopenharmony_ci	tty_encode_baud_rate(tty, 115200, 115200);
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	int result;
11158c2ecf20Sopenharmony_ci	/* We are done with this URB, resubmit it. Prep the USB to wait for
11168c2ecf20Sopenharmony_ci	 * another frame */
11178c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, serial->parent->usb,
11188c2ecf20Sopenharmony_ci			  usb_rcvbulkpipe(serial->parent->usb,
11198c2ecf20Sopenharmony_ci					  serial->in_endp->
11208c2ecf20Sopenharmony_ci					  bEndpointAddress & 0x7F),
11218c2ecf20Sopenharmony_ci			  urb->transfer_buffer, serial->rx_data_length,
11228c2ecf20Sopenharmony_ci			  hso_std_serial_read_bulk_callback, serial);
11238c2ecf20Sopenharmony_ci	/* Give this to the USB subsystem so it can tell us when more data
11248c2ecf20Sopenharmony_ci	 * arrives. */
11258c2ecf20Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
11268c2ecf20Sopenharmony_ci	if (result) {
11278c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n",
11288c2ecf20Sopenharmony_ci			__func__, result);
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_cistatic void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial)
11368c2ecf20Sopenharmony_ci{
11378c2ecf20Sopenharmony_ci	int count;
11388c2ecf20Sopenharmony_ci	struct urb *curr_urb;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) {
11418c2ecf20Sopenharmony_ci		curr_urb = serial->rx_urb[serial->curr_rx_urb_idx];
11428c2ecf20Sopenharmony_ci		count = put_rxbuf_data(curr_urb, serial);
11438c2ecf20Sopenharmony_ci		if (count == -1)
11448c2ecf20Sopenharmony_ci			return;
11458c2ecf20Sopenharmony_ci		if (count == 0) {
11468c2ecf20Sopenharmony_ci			serial->curr_rx_urb_idx++;
11478c2ecf20Sopenharmony_ci			if (serial->curr_rx_urb_idx >= serial->num_rx_urbs)
11488c2ecf20Sopenharmony_ci				serial->curr_rx_urb_idx = 0;
11498c2ecf20Sopenharmony_ci			hso_resubmit_rx_bulk_urb(serial, curr_urb);
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistatic void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	int count = 0;
11578c2ecf20Sopenharmony_ci	struct urb *urb;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	urb = serial->rx_urb[0];
11608c2ecf20Sopenharmony_ci	if (serial->port.count > 0) {
11618c2ecf20Sopenharmony_ci		count = put_rxbuf_data(urb, serial);
11628c2ecf20Sopenharmony_ci		if (count == -1)
11638c2ecf20Sopenharmony_ci			return;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci	/* Re issue a read as long as we receive data. */
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (count == 0 && ((urb->actual_length != 0) ||
11688c2ecf20Sopenharmony_ci			   (serial->rx_state == RX_PENDING))) {
11698c2ecf20Sopenharmony_ci		serial->rx_state = RX_SENT;
11708c2ecf20Sopenharmony_ci		hso_mux_serial_read(serial);
11718c2ecf20Sopenharmony_ci	} else
11728c2ecf20Sopenharmony_ci		serial->rx_state = RX_IDLE;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci/* read callback for Diag and CS port */
11778c2ecf20Sopenharmony_cistatic void hso_std_serial_read_bulk_callback(struct urb *urb)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	struct hso_serial *serial = urb->context;
11808c2ecf20Sopenharmony_ci	int status = urb->status;
11818c2ecf20Sopenharmony_ci	unsigned long flags;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	hso_dbg(0x8, "--- Got serial_read_bulk callback %02x ---\n", status);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	/* sanity check */
11868c2ecf20Sopenharmony_ci	if (!serial) {
11878c2ecf20Sopenharmony_ci		hso_dbg(0x1, "serial == NULL\n");
11888c2ecf20Sopenharmony_ci		return;
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci	if (status) {
11918c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, serial->parent);
11928c2ecf20Sopenharmony_ci		return;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	hso_dbg(0x1, "Actual length = %d\n", urb->actual_length);
11968c2ecf20Sopenharmony_ci	DUMP1(urb->transfer_buffer, urb->actual_length);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	/* Anyone listening? */
11998c2ecf20Sopenharmony_ci	if (serial->port.count == 0)
12008c2ecf20Sopenharmony_ci		return;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (serial->parent->port_spec & HSO_INFO_CRC_BUG)
12038c2ecf20Sopenharmony_ci		fix_crc_bug(urb, serial->in_endp->wMaxPacketSize);
12048c2ecf20Sopenharmony_ci	/* Valid data, handle RX data */
12058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
12068c2ecf20Sopenharmony_ci	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1;
12078c2ecf20Sopenharmony_ci	put_rxbuf_data_and_resubmit_bulk_urb(serial);
12088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci/*
12128c2ecf20Sopenharmony_ci * This needs to be a tasklet otherwise we will
12138c2ecf20Sopenharmony_ci * end up recursively calling this function.
12148c2ecf20Sopenharmony_ci */
12158c2ecf20Sopenharmony_cistatic void hso_unthrottle_tasklet(unsigned long data)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	struct hso_serial *serial = (struct hso_serial *)data;
12188c2ecf20Sopenharmony_ci	unsigned long flags;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
12218c2ecf20Sopenharmony_ci	if ((serial->parent->port_spec & HSO_INTF_MUX))
12228c2ecf20Sopenharmony_ci		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
12238c2ecf20Sopenharmony_ci	else
12248c2ecf20Sopenharmony_ci		put_rxbuf_data_and_resubmit_bulk_urb(serial);
12258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cistatic	void hso_unthrottle(struct tty_struct *tty)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	tasklet_hi_schedule(&serial->unthrottle_tasklet);
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci/* open the requested serial port */
12368c2ecf20Sopenharmony_cistatic int hso_serial_open(struct tty_struct *tty, struct file *filp)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	struct hso_serial *serial = get_serial_by_index(tty->index);
12398c2ecf20Sopenharmony_ci	int result;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* sanity check */
12428c2ecf20Sopenharmony_ci	if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) {
12438c2ecf20Sopenharmony_ci		WARN_ON(1);
12448c2ecf20Sopenharmony_ci		tty->driver_data = NULL;
12458c2ecf20Sopenharmony_ci		hso_dbg(0x1, "Failed to open port\n");
12468c2ecf20Sopenharmony_ci		return -ENODEV;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	mutex_lock(&serial->parent->mutex);
12508c2ecf20Sopenharmony_ci	result = usb_autopm_get_interface(serial->parent->interface);
12518c2ecf20Sopenharmony_ci	if (result < 0)
12528c2ecf20Sopenharmony_ci		goto err_out;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	hso_dbg(0x1, "Opening %d\n", serial->minor);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	/* setup */
12578c2ecf20Sopenharmony_ci	tty->driver_data = serial;
12588c2ecf20Sopenharmony_ci	tty_port_tty_set(&serial->port, tty);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/* check for port already opened, if not set the termios */
12618c2ecf20Sopenharmony_ci	serial->port.count++;
12628c2ecf20Sopenharmony_ci	if (serial->port.count == 1) {
12638c2ecf20Sopenharmony_ci		serial->rx_state = RX_IDLE;
12648c2ecf20Sopenharmony_ci		/* Force default termio settings */
12658c2ecf20Sopenharmony_ci		_hso_serial_set_termios(tty, NULL);
12668c2ecf20Sopenharmony_ci		tasklet_init(&serial->unthrottle_tasklet,
12678c2ecf20Sopenharmony_ci			     hso_unthrottle_tasklet,
12688c2ecf20Sopenharmony_ci			     (unsigned long)serial);
12698c2ecf20Sopenharmony_ci		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
12708c2ecf20Sopenharmony_ci		if (result) {
12718c2ecf20Sopenharmony_ci			hso_stop_serial_device(serial->parent);
12728c2ecf20Sopenharmony_ci			serial->port.count--;
12738c2ecf20Sopenharmony_ci		} else {
12748c2ecf20Sopenharmony_ci			kref_get(&serial->parent->ref);
12758c2ecf20Sopenharmony_ci		}
12768c2ecf20Sopenharmony_ci	} else {
12778c2ecf20Sopenharmony_ci		hso_dbg(0x1, "Port was already open\n");
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	usb_autopm_put_interface(serial->parent->interface);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* done */
12838c2ecf20Sopenharmony_ci	if (result)
12848c2ecf20Sopenharmony_ci		hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0);
12858c2ecf20Sopenharmony_cierr_out:
12868c2ecf20Sopenharmony_ci	mutex_unlock(&serial->parent->mutex);
12878c2ecf20Sopenharmony_ci	return result;
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci/* close the requested serial port */
12918c2ecf20Sopenharmony_cistatic void hso_serial_close(struct tty_struct *tty, struct file *filp)
12928c2ecf20Sopenharmony_ci{
12938c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
12948c2ecf20Sopenharmony_ci	u8 usb_gone;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	hso_dbg(0x1, "Closing serial port\n");
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	/* Open failed, no close cleanup required */
12998c2ecf20Sopenharmony_ci	if (serial == NULL)
13008c2ecf20Sopenharmony_ci		return;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	mutex_lock(&serial->parent->mutex);
13038c2ecf20Sopenharmony_ci	usb_gone = serial->parent->usb_gone;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	if (!usb_gone)
13068c2ecf20Sopenharmony_ci		usb_autopm_get_interface(serial->parent->interface);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	/* reset the rts and dtr */
13098c2ecf20Sopenharmony_ci	/* do the actual close */
13108c2ecf20Sopenharmony_ci	serial->port.count--;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	if (serial->port.count <= 0) {
13138c2ecf20Sopenharmony_ci		serial->port.count = 0;
13148c2ecf20Sopenharmony_ci		tty_port_tty_set(&serial->port, NULL);
13158c2ecf20Sopenharmony_ci		if (!usb_gone)
13168c2ecf20Sopenharmony_ci			hso_stop_serial_device(serial->parent);
13178c2ecf20Sopenharmony_ci		tasklet_kill(&serial->unthrottle_tasklet);
13188c2ecf20Sopenharmony_ci	}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	if (!usb_gone)
13218c2ecf20Sopenharmony_ci		usb_autopm_put_interface(serial->parent->interface);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	mutex_unlock(&serial->parent->mutex);
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci/* close the requested serial port */
13278c2ecf20Sopenharmony_cistatic int hso_serial_write(struct tty_struct *tty, const unsigned char *buf,
13288c2ecf20Sopenharmony_ci			    int count)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
13318c2ecf20Sopenharmony_ci	int space, tx_bytes;
13328c2ecf20Sopenharmony_ci	unsigned long flags;
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	/* sanity check */
13358c2ecf20Sopenharmony_ci	if (serial == NULL) {
13368c2ecf20Sopenharmony_ci		pr_err("%s: serial is NULL\n", __func__);
13378c2ecf20Sopenharmony_ci		return -ENODEV;
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	space = serial->tx_data_length - serial->tx_buffer_count;
13438c2ecf20Sopenharmony_ci	tx_bytes = (count < space) ? count : space;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	if (!tx_bytes)
13468c2ecf20Sopenharmony_ci		goto out;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes);
13498c2ecf20Sopenharmony_ci	serial->tx_buffer_count += tx_bytes;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ciout:
13528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	hso_kick_transmit(serial);
13558c2ecf20Sopenharmony_ci	/* done */
13568c2ecf20Sopenharmony_ci	return tx_bytes;
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci/* how much room is there for writing */
13608c2ecf20Sopenharmony_cistatic int hso_serial_write_room(struct tty_struct *tty)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
13638c2ecf20Sopenharmony_ci	int room;
13648c2ecf20Sopenharmony_ci	unsigned long flags;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
13678c2ecf20Sopenharmony_ci	room = serial->tx_data_length - serial->tx_buffer_count;
13688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	/* return free room */
13718c2ecf20Sopenharmony_ci	return room;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic void hso_serial_cleanup(struct tty_struct *tty)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	if (!serial)
13798c2ecf20Sopenharmony_ci		return;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	kref_put(&serial->parent->ref, hso_serial_ref_free);
13828c2ecf20Sopenharmony_ci}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci/* setup the term */
13858c2ecf20Sopenharmony_cistatic void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
13868c2ecf20Sopenharmony_ci{
13878c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
13888c2ecf20Sopenharmony_ci	unsigned long flags;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	if (old)
13918c2ecf20Sopenharmony_ci		hso_dbg(0x16, "Termios called with: cflags new[%u] - old[%u]\n",
13928c2ecf20Sopenharmony_ci			(unsigned int)tty->termios.c_cflag,
13938c2ecf20Sopenharmony_ci			(unsigned int)old->c_cflag);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	/* the actual setup */
13968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
13978c2ecf20Sopenharmony_ci	if (serial->port.count)
13988c2ecf20Sopenharmony_ci		_hso_serial_set_termios(tty, old);
13998c2ecf20Sopenharmony_ci	else
14008c2ecf20Sopenharmony_ci		tty->termios = *old;
14018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* done */
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci/* how many characters in the buffer */
14078c2ecf20Sopenharmony_cistatic int hso_serial_chars_in_buffer(struct tty_struct *tty)
14088c2ecf20Sopenharmony_ci{
14098c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
14108c2ecf20Sopenharmony_ci	int chars;
14118c2ecf20Sopenharmony_ci	unsigned long flags;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* sanity check */
14148c2ecf20Sopenharmony_ci	if (serial == NULL)
14158c2ecf20Sopenharmony_ci		return 0;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
14188c2ecf20Sopenharmony_ci	chars = serial->tx_buffer_count;
14198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	return chars;
14228c2ecf20Sopenharmony_ci}
14238c2ecf20Sopenharmony_cistatic int tiocmget_submit_urb(struct hso_serial *serial,
14248c2ecf20Sopenharmony_ci			       struct hso_tiocmget *tiocmget,
14258c2ecf20Sopenharmony_ci			       struct usb_device *usb)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	int result;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	if (serial->parent->usb_gone)
14308c2ecf20Sopenharmony_ci		return -ENODEV;
14318c2ecf20Sopenharmony_ci	usb_fill_int_urb(tiocmget->urb, usb,
14328c2ecf20Sopenharmony_ci			 usb_rcvintpipe(usb,
14338c2ecf20Sopenharmony_ci					tiocmget->endp->
14348c2ecf20Sopenharmony_ci					bEndpointAddress & 0x7F),
14358c2ecf20Sopenharmony_ci			 tiocmget->serial_state_notification,
14368c2ecf20Sopenharmony_ci			 sizeof(struct hso_serial_state_notification),
14378c2ecf20Sopenharmony_ci			 tiocmget_intr_callback, serial,
14388c2ecf20Sopenharmony_ci			 tiocmget->endp->bInterval);
14398c2ecf20Sopenharmony_ci	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
14408c2ecf20Sopenharmony_ci	if (result) {
14418c2ecf20Sopenharmony_ci		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
14428c2ecf20Sopenharmony_ci			 result);
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci	return result;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cistatic void tiocmget_intr_callback(struct urb *urb)
14498c2ecf20Sopenharmony_ci{
14508c2ecf20Sopenharmony_ci	struct hso_serial *serial = urb->context;
14518c2ecf20Sopenharmony_ci	struct hso_tiocmget *tiocmget;
14528c2ecf20Sopenharmony_ci	int status = urb->status;
14538c2ecf20Sopenharmony_ci	u16 UART_state_bitmap, prev_UART_state_bitmap;
14548c2ecf20Sopenharmony_ci	struct uart_icount *icount;
14558c2ecf20Sopenharmony_ci	struct hso_serial_state_notification *serial_state_notification;
14568c2ecf20Sopenharmony_ci	struct usb_device *usb;
14578c2ecf20Sopenharmony_ci	struct usb_interface *interface;
14588c2ecf20Sopenharmony_ci	int if_num;
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	/* Sanity checks */
14618c2ecf20Sopenharmony_ci	if (!serial)
14628c2ecf20Sopenharmony_ci		return;
14638c2ecf20Sopenharmony_ci	if (status) {
14648c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, serial->parent);
14658c2ecf20Sopenharmony_ci		return;
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	/* tiocmget is only supported on HSO_PORT_MODEM */
14698c2ecf20Sopenharmony_ci	tiocmget = serial->tiocmget;
14708c2ecf20Sopenharmony_ci	if (!tiocmget)
14718c2ecf20Sopenharmony_ci		return;
14728c2ecf20Sopenharmony_ci	BUG_ON((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	usb = serial->parent->usb;
14758c2ecf20Sopenharmony_ci	interface = serial->parent->interface;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	/* wIndex should be the USB interface number of the port to which the
14808c2ecf20Sopenharmony_ci	 * notification applies, which should always be the Modem port.
14818c2ecf20Sopenharmony_ci	 */
14828c2ecf20Sopenharmony_ci	serial_state_notification = tiocmget->serial_state_notification;
14838c2ecf20Sopenharmony_ci	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
14848c2ecf20Sopenharmony_ci	    serial_state_notification->bNotification != B_NOTIFICATION ||
14858c2ecf20Sopenharmony_ci	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
14868c2ecf20Sopenharmony_ci	    le16_to_cpu(serial_state_notification->wIndex) != if_num ||
14878c2ecf20Sopenharmony_ci	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
14888c2ecf20Sopenharmony_ci		dev_warn(&usb->dev,
14898c2ecf20Sopenharmony_ci			 "hso received invalid serial state notification\n");
14908c2ecf20Sopenharmony_ci		DUMP(serial_state_notification,
14918c2ecf20Sopenharmony_ci		     sizeof(struct hso_serial_state_notification));
14928c2ecf20Sopenharmony_ci	} else {
14938c2ecf20Sopenharmony_ci		unsigned long flags;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci		UART_state_bitmap = le16_to_cpu(serial_state_notification->
14968c2ecf20Sopenharmony_ci						UART_state_bitmap);
14978c2ecf20Sopenharmony_ci		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
14988c2ecf20Sopenharmony_ci		icount = &tiocmget->icount;
14998c2ecf20Sopenharmony_ci		spin_lock_irqsave(&serial->serial_lock, flags);
15008c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_OVERRUN) !=
15018c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_OVERRUN))
15028c2ecf20Sopenharmony_ci			icount->parity++;
15038c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_PARITY) !=
15048c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_PARITY))
15058c2ecf20Sopenharmony_ci			icount->parity++;
15068c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_FRAMING) !=
15078c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_FRAMING))
15088c2ecf20Sopenharmony_ci			icount->frame++;
15098c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_RING_SIGNAL) &&
15108c2ecf20Sopenharmony_ci		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
15118c2ecf20Sopenharmony_ci			icount->rng++;
15128c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_BREAK) !=
15138c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_BREAK))
15148c2ecf20Sopenharmony_ci			icount->brk++;
15158c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_TX_CARRIER) !=
15168c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_TX_CARRIER))
15178c2ecf20Sopenharmony_ci			icount->dsr++;
15188c2ecf20Sopenharmony_ci		if ((UART_state_bitmap & B_RX_CARRIER) !=
15198c2ecf20Sopenharmony_ci		   (prev_UART_state_bitmap & B_RX_CARRIER))
15208c2ecf20Sopenharmony_ci			icount->dcd++;
15218c2ecf20Sopenharmony_ci		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
15228c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&serial->serial_lock, flags);
15238c2ecf20Sopenharmony_ci		tiocmget->intr_completed = 1;
15248c2ecf20Sopenharmony_ci		wake_up_interruptible(&tiocmget->waitq);
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci	memset(serial_state_notification, 0,
15278c2ecf20Sopenharmony_ci	       sizeof(struct hso_serial_state_notification));
15288c2ecf20Sopenharmony_ci	tiocmget_submit_urb(serial,
15298c2ecf20Sopenharmony_ci			    tiocmget,
15308c2ecf20Sopenharmony_ci			    serial->parent->usb);
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci/*
15348c2ecf20Sopenharmony_ci * next few functions largely stolen from drivers/serial/serial_core.c
15358c2ecf20Sopenharmony_ci */
15368c2ecf20Sopenharmony_ci/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
15378c2ecf20Sopenharmony_ci * - mask passed in arg for lines of interest
15388c2ecf20Sopenharmony_ci *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
15398c2ecf20Sopenharmony_ci * Caller should use TIOCGICOUNT to see which one it was
15408c2ecf20Sopenharmony_ci */
15418c2ecf20Sopenharmony_cistatic int
15428c2ecf20Sopenharmony_cihso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
15438c2ecf20Sopenharmony_ci{
15448c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
15458c2ecf20Sopenharmony_ci	struct uart_icount cprev, cnow;
15468c2ecf20Sopenharmony_ci	struct hso_tiocmget  *tiocmget;
15478c2ecf20Sopenharmony_ci	int ret;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	tiocmget = serial->tiocmget;
15508c2ecf20Sopenharmony_ci	if (!tiocmget)
15518c2ecf20Sopenharmony_ci		return -ENOENT;
15528c2ecf20Sopenharmony_ci	/*
15538c2ecf20Sopenharmony_ci	 * note the counters on entry
15548c2ecf20Sopenharmony_ci	 */
15558c2ecf20Sopenharmony_ci	spin_lock_irq(&serial->serial_lock);
15568c2ecf20Sopenharmony_ci	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
15578c2ecf20Sopenharmony_ci	spin_unlock_irq(&serial->serial_lock);
15588c2ecf20Sopenharmony_ci	add_wait_queue(&tiocmget->waitq, &wait);
15598c2ecf20Sopenharmony_ci	for (;;) {
15608c2ecf20Sopenharmony_ci		spin_lock_irq(&serial->serial_lock);
15618c2ecf20Sopenharmony_ci		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
15628c2ecf20Sopenharmony_ci		spin_unlock_irq(&serial->serial_lock);
15638c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
15648c2ecf20Sopenharmony_ci		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
15658c2ecf20Sopenharmony_ci		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
15668c2ecf20Sopenharmony_ci		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
15678c2ecf20Sopenharmony_ci			ret = 0;
15688c2ecf20Sopenharmony_ci			break;
15698c2ecf20Sopenharmony_ci		}
15708c2ecf20Sopenharmony_ci		schedule();
15718c2ecf20Sopenharmony_ci		/* see if a signal did it */
15728c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
15738c2ecf20Sopenharmony_ci			ret = -ERESTARTSYS;
15748c2ecf20Sopenharmony_ci			break;
15758c2ecf20Sopenharmony_ci		}
15768c2ecf20Sopenharmony_ci		cprev = cnow;
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci	__set_current_state(TASK_RUNNING);
15798c2ecf20Sopenharmony_ci	remove_wait_queue(&tiocmget->waitq, &wait);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return ret;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci/*
15858c2ecf20Sopenharmony_ci * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
15868c2ecf20Sopenharmony_ci * Return: write counters to the user passed counter struct
15878c2ecf20Sopenharmony_ci * NB: both 1->0 and 0->1 transitions are counted except for
15888c2ecf20Sopenharmony_ci *     RI where only 0->1 is counted.
15898c2ecf20Sopenharmony_ci */
15908c2ecf20Sopenharmony_cistatic int hso_get_count(struct tty_struct *tty,
15918c2ecf20Sopenharmony_ci		  struct serial_icounter_struct *icount)
15928c2ecf20Sopenharmony_ci{
15938c2ecf20Sopenharmony_ci	struct uart_icount cnow;
15948c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
15958c2ecf20Sopenharmony_ci	struct hso_tiocmget  *tiocmget = serial->tiocmget;
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	memset(icount, 0, sizeof(struct serial_icounter_struct));
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	if (!tiocmget)
16008c2ecf20Sopenharmony_ci		 return -ENOENT;
16018c2ecf20Sopenharmony_ci	spin_lock_irq(&serial->serial_lock);
16028c2ecf20Sopenharmony_ci	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
16038c2ecf20Sopenharmony_ci	spin_unlock_irq(&serial->serial_lock);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	icount->cts         = cnow.cts;
16068c2ecf20Sopenharmony_ci	icount->dsr         = cnow.dsr;
16078c2ecf20Sopenharmony_ci	icount->rng         = cnow.rng;
16088c2ecf20Sopenharmony_ci	icount->dcd         = cnow.dcd;
16098c2ecf20Sopenharmony_ci	icount->rx          = cnow.rx;
16108c2ecf20Sopenharmony_ci	icount->tx          = cnow.tx;
16118c2ecf20Sopenharmony_ci	icount->frame       = cnow.frame;
16128c2ecf20Sopenharmony_ci	icount->overrun     = cnow.overrun;
16138c2ecf20Sopenharmony_ci	icount->parity      = cnow.parity;
16148c2ecf20Sopenharmony_ci	icount->brk         = cnow.brk;
16158c2ecf20Sopenharmony_ci	icount->buf_overrun = cnow.buf_overrun;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	return 0;
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic int hso_serial_tiocmget(struct tty_struct *tty)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	int retval;
16248c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
16258c2ecf20Sopenharmony_ci	struct hso_tiocmget  *tiocmget;
16268c2ecf20Sopenharmony_ci	u16 UART_state_bitmap;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	/* sanity check */
16298c2ecf20Sopenharmony_ci	if (!serial) {
16308c2ecf20Sopenharmony_ci		hso_dbg(0x1, "no tty structures\n");
16318c2ecf20Sopenharmony_ci		return -EINVAL;
16328c2ecf20Sopenharmony_ci	}
16338c2ecf20Sopenharmony_ci	spin_lock_irq(&serial->serial_lock);
16348c2ecf20Sopenharmony_ci	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
16358c2ecf20Sopenharmony_ci	    ((serial->dtr_state) ? TIOCM_DTR : 0);
16368c2ecf20Sopenharmony_ci	tiocmget = serial->tiocmget;
16378c2ecf20Sopenharmony_ci	if (tiocmget) {
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci		UART_state_bitmap = le16_to_cpu(
16408c2ecf20Sopenharmony_ci			tiocmget->prev_UART_state_bitmap);
16418c2ecf20Sopenharmony_ci		if (UART_state_bitmap & B_RING_SIGNAL)
16428c2ecf20Sopenharmony_ci			retval |=  TIOCM_RNG;
16438c2ecf20Sopenharmony_ci		if (UART_state_bitmap & B_RX_CARRIER)
16448c2ecf20Sopenharmony_ci			retval |=  TIOCM_CD;
16458c2ecf20Sopenharmony_ci		if (UART_state_bitmap & B_TX_CARRIER)
16468c2ecf20Sopenharmony_ci			retval |=  TIOCM_DSR;
16478c2ecf20Sopenharmony_ci	}
16488c2ecf20Sopenharmony_ci	spin_unlock_irq(&serial->serial_lock);
16498c2ecf20Sopenharmony_ci	return retval;
16508c2ecf20Sopenharmony_ci}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cistatic int hso_serial_tiocmset(struct tty_struct *tty,
16538c2ecf20Sopenharmony_ci			       unsigned int set, unsigned int clear)
16548c2ecf20Sopenharmony_ci{
16558c2ecf20Sopenharmony_ci	int val = 0;
16568c2ecf20Sopenharmony_ci	unsigned long flags;
16578c2ecf20Sopenharmony_ci	int if_num;
16588c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
16598c2ecf20Sopenharmony_ci	struct usb_interface *interface;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	/* sanity check */
16628c2ecf20Sopenharmony_ci	if (!serial) {
16638c2ecf20Sopenharmony_ci		hso_dbg(0x1, "no tty structures\n");
16648c2ecf20Sopenharmony_ci		return -EINVAL;
16658c2ecf20Sopenharmony_ci	}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	if ((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM)
16688c2ecf20Sopenharmony_ci		return -EINVAL;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	interface = serial->parent->interface;
16718c2ecf20Sopenharmony_ci	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
16748c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
16758c2ecf20Sopenharmony_ci		serial->rts_state = 1;
16768c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
16778c2ecf20Sopenharmony_ci		serial->dtr_state = 1;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
16808c2ecf20Sopenharmony_ci		serial->rts_state = 0;
16818c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
16828c2ecf20Sopenharmony_ci		serial->dtr_state = 0;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	if (serial->dtr_state)
16858c2ecf20Sopenharmony_ci		val |= 0x01;
16868c2ecf20Sopenharmony_ci	if (serial->rts_state)
16878c2ecf20Sopenharmony_ci		val |= 0x02;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	return usb_control_msg(serial->parent->usb,
16928c2ecf20Sopenharmony_ci			       usb_sndctrlpipe(serial->parent->usb, 0), 0x22,
16938c2ecf20Sopenharmony_ci			       0x21, val, if_num, NULL, 0,
16948c2ecf20Sopenharmony_ci			       USB_CTRL_SET_TIMEOUT);
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic int hso_serial_ioctl(struct tty_struct *tty,
16988c2ecf20Sopenharmony_ci			    unsigned int cmd, unsigned long arg)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	struct hso_serial *serial = tty->driver_data;
17018c2ecf20Sopenharmony_ci	int ret = 0;
17028c2ecf20Sopenharmony_ci	hso_dbg(0x8, "IOCTL cmd: %d, arg: %ld\n", cmd, arg);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	if (!serial)
17058c2ecf20Sopenharmony_ci		return -ENODEV;
17068c2ecf20Sopenharmony_ci	switch (cmd) {
17078c2ecf20Sopenharmony_ci	case TIOCMIWAIT:
17088c2ecf20Sopenharmony_ci		ret = hso_wait_modem_status(serial, arg);
17098c2ecf20Sopenharmony_ci		break;
17108c2ecf20Sopenharmony_ci	default:
17118c2ecf20Sopenharmony_ci		ret = -ENOIOCTLCMD;
17128c2ecf20Sopenharmony_ci		break;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci	return ret;
17158c2ecf20Sopenharmony_ci}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci/* starts a transmit */
17198c2ecf20Sopenharmony_cistatic void hso_kick_transmit(struct hso_serial *serial)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	unsigned long flags;
17228c2ecf20Sopenharmony_ci	int res;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
17258c2ecf20Sopenharmony_ci	if (!serial->tx_buffer_count)
17268c2ecf20Sopenharmony_ci		goto out;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	if (serial->tx_urb_used)
17298c2ecf20Sopenharmony_ci		goto out;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	/* Wakeup USB interface if necessary */
17328c2ecf20Sopenharmony_ci	if (hso_get_activity(serial->parent) == -EAGAIN)
17338c2ecf20Sopenharmony_ci		goto out;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	/* Switch pointers around to avoid memcpy */
17368c2ecf20Sopenharmony_ci	swap(serial->tx_buffer, serial->tx_data);
17378c2ecf20Sopenharmony_ci	serial->tx_data_count = serial->tx_buffer_count;
17388c2ecf20Sopenharmony_ci	serial->tx_buffer_count = 0;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/* If serial->tx_data is set, it means we switched buffers */
17418c2ecf20Sopenharmony_ci	if (serial->tx_data && serial->write_data) {
17428c2ecf20Sopenharmony_ci		res = serial->write_data(serial);
17438c2ecf20Sopenharmony_ci		if (res >= 0)
17448c2ecf20Sopenharmony_ci			serial->tx_urb_used = 1;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ciout:
17478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
17488c2ecf20Sopenharmony_ci}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci/* make a request (for reading and writing data to muxed serial port) */
17518c2ecf20Sopenharmony_cistatic int mux_device_request(struct hso_serial *serial, u8 type, u16 port,
17528c2ecf20Sopenharmony_ci			      struct urb *ctrl_urb,
17538c2ecf20Sopenharmony_ci			      struct usb_ctrlrequest *ctrl_req,
17548c2ecf20Sopenharmony_ci			      u8 *ctrl_urb_data, u32 size)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	int result;
17578c2ecf20Sopenharmony_ci	int pipe;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	/* Sanity check */
17608c2ecf20Sopenharmony_ci	if (!serial || !ctrl_urb || !ctrl_req) {
17618c2ecf20Sopenharmony_ci		pr_err("%s: Wrong arguments\n", __func__);
17628c2ecf20Sopenharmony_ci		return -EINVAL;
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	/* initialize */
17668c2ecf20Sopenharmony_ci	ctrl_req->wValue = 0;
17678c2ecf20Sopenharmony_ci	ctrl_req->wIndex = cpu_to_le16(hso_port_to_mux(port));
17688c2ecf20Sopenharmony_ci	ctrl_req->wLength = cpu_to_le16(size);
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) {
17718c2ecf20Sopenharmony_ci		/* Reading command */
17728c2ecf20Sopenharmony_ci		ctrl_req->bRequestType = USB_DIR_IN |
17738c2ecf20Sopenharmony_ci					 USB_TYPE_OPTION_VENDOR |
17748c2ecf20Sopenharmony_ci					 USB_RECIP_INTERFACE;
17758c2ecf20Sopenharmony_ci		ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
17768c2ecf20Sopenharmony_ci		pipe = usb_rcvctrlpipe(serial->parent->usb, 0);
17778c2ecf20Sopenharmony_ci	} else {
17788c2ecf20Sopenharmony_ci		/* Writing command */
17798c2ecf20Sopenharmony_ci		ctrl_req->bRequestType = USB_DIR_OUT |
17808c2ecf20Sopenharmony_ci					 USB_TYPE_OPTION_VENDOR |
17818c2ecf20Sopenharmony_ci					 USB_RECIP_INTERFACE;
17828c2ecf20Sopenharmony_ci		ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
17838c2ecf20Sopenharmony_ci		pipe = usb_sndctrlpipe(serial->parent->usb, 0);
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci	/* syslog */
17868c2ecf20Sopenharmony_ci	hso_dbg(0x2, "%s command (%02x) len: %d, port: %d\n",
17878c2ecf20Sopenharmony_ci		type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write",
17888c2ecf20Sopenharmony_ci		ctrl_req->bRequestType, ctrl_req->wLength, port);
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	/* Load ctrl urb */
17918c2ecf20Sopenharmony_ci	ctrl_urb->transfer_flags = 0;
17928c2ecf20Sopenharmony_ci	usb_fill_control_urb(ctrl_urb,
17938c2ecf20Sopenharmony_ci			     serial->parent->usb,
17948c2ecf20Sopenharmony_ci			     pipe,
17958c2ecf20Sopenharmony_ci			     (u8 *) ctrl_req,
17968c2ecf20Sopenharmony_ci			     ctrl_urb_data, size, ctrl_callback, serial);
17978c2ecf20Sopenharmony_ci	/* Send it on merry way */
17988c2ecf20Sopenharmony_ci	result = usb_submit_urb(ctrl_urb, GFP_ATOMIC);
17998c2ecf20Sopenharmony_ci	if (result) {
18008c2ecf20Sopenharmony_ci		dev_err(&ctrl_urb->dev->dev,
18018c2ecf20Sopenharmony_ci			"%s failed submit ctrl_urb %d type %d\n", __func__,
18028c2ecf20Sopenharmony_ci			result, type);
18038c2ecf20Sopenharmony_ci		return result;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	/* done */
18078c2ecf20Sopenharmony_ci	return size;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci/* called by intr_callback when read occurs */
18118c2ecf20Sopenharmony_cistatic int hso_mux_serial_read(struct hso_serial *serial)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	if (!serial)
18148c2ecf20Sopenharmony_ci		return -EINVAL;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	/* clean data */
18178c2ecf20Sopenharmony_ci	memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE);
18188c2ecf20Sopenharmony_ci	/* make the request */
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	if (serial->num_rx_urbs != 1) {
18218c2ecf20Sopenharmony_ci		dev_err(&serial->parent->interface->dev,
18228c2ecf20Sopenharmony_ci			"ERROR: mux'd reads with multiple buffers "
18238c2ecf20Sopenharmony_ci			"not possible\n");
18248c2ecf20Sopenharmony_ci		return 0;
18258c2ecf20Sopenharmony_ci	}
18268c2ecf20Sopenharmony_ci	return mux_device_request(serial,
18278c2ecf20Sopenharmony_ci				  USB_CDC_GET_ENCAPSULATED_RESPONSE,
18288c2ecf20Sopenharmony_ci				  serial->parent->port_spec & HSO_PORT_MASK,
18298c2ecf20Sopenharmony_ci				  serial->rx_urb[0],
18308c2ecf20Sopenharmony_ci				  &serial->ctrl_req_rx,
18318c2ecf20Sopenharmony_ci				  serial->rx_data[0], serial->rx_data_length);
18328c2ecf20Sopenharmony_ci}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci/* used for muxed serial port callback (muxed serial read) */
18358c2ecf20Sopenharmony_cistatic void intr_callback(struct urb *urb)
18368c2ecf20Sopenharmony_ci{
18378c2ecf20Sopenharmony_ci	struct hso_shared_int *shared_int = urb->context;
18388c2ecf20Sopenharmony_ci	struct hso_serial *serial;
18398c2ecf20Sopenharmony_ci	unsigned char *port_req;
18408c2ecf20Sopenharmony_ci	int status = urb->status;
18418c2ecf20Sopenharmony_ci	unsigned long flags;
18428c2ecf20Sopenharmony_ci	int i;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	usb_mark_last_busy(urb->dev);
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	/* sanity check */
18478c2ecf20Sopenharmony_ci	if (!shared_int)
18488c2ecf20Sopenharmony_ci		return;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	/* status check */
18518c2ecf20Sopenharmony_ci	if (status) {
18528c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, NULL);
18538c2ecf20Sopenharmony_ci		return;
18548c2ecf20Sopenharmony_ci	}
18558c2ecf20Sopenharmony_ci	hso_dbg(0x8, "--- Got intr callback 0x%02X ---\n", status);
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	/* what request? */
18588c2ecf20Sopenharmony_ci	port_req = urb->transfer_buffer;
18598c2ecf20Sopenharmony_ci	hso_dbg(0x8, "port_req = 0x%.2X\n", *port_req);
18608c2ecf20Sopenharmony_ci	/* loop over all muxed ports to find the one sending this */
18618c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
18628c2ecf20Sopenharmony_ci		/* max 8 channels on MUX */
18638c2ecf20Sopenharmony_ci		if (*port_req & (1 << i)) {
18648c2ecf20Sopenharmony_ci			serial = get_serial_by_shared_int_and_type(shared_int,
18658c2ecf20Sopenharmony_ci								   (1 << i));
18668c2ecf20Sopenharmony_ci			if (serial != NULL) {
18678c2ecf20Sopenharmony_ci				hso_dbg(0x1, "Pending read interrupt on port %d\n",
18688c2ecf20Sopenharmony_ci					i);
18698c2ecf20Sopenharmony_ci				spin_lock_irqsave(&serial->serial_lock, flags);
18708c2ecf20Sopenharmony_ci				if (serial->rx_state == RX_IDLE &&
18718c2ecf20Sopenharmony_ci					serial->port.count > 0) {
18728c2ecf20Sopenharmony_ci					/* Setup and send a ctrl req read on
18738c2ecf20Sopenharmony_ci					 * port i */
18748c2ecf20Sopenharmony_ci					if (!serial->rx_urb_filled[0]) {
18758c2ecf20Sopenharmony_ci						serial->rx_state = RX_SENT;
18768c2ecf20Sopenharmony_ci						hso_mux_serial_read(serial);
18778c2ecf20Sopenharmony_ci					} else
18788c2ecf20Sopenharmony_ci						serial->rx_state = RX_PENDING;
18798c2ecf20Sopenharmony_ci				} else {
18808c2ecf20Sopenharmony_ci					hso_dbg(0x1, "Already a read pending on port %d or port not open\n",
18818c2ecf20Sopenharmony_ci						i);
18828c2ecf20Sopenharmony_ci				}
18838c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&serial->serial_lock,
18848c2ecf20Sopenharmony_ci						       flags);
18858c2ecf20Sopenharmony_ci			}
18868c2ecf20Sopenharmony_ci		}
18878c2ecf20Sopenharmony_ci	}
18888c2ecf20Sopenharmony_ci	/* Resubmit interrupt urb */
18898c2ecf20Sopenharmony_ci	hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC);
18908c2ecf20Sopenharmony_ci}
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci/* called for writing to muxed serial port */
18938c2ecf20Sopenharmony_cistatic int hso_mux_serial_write_data(struct hso_serial *serial)
18948c2ecf20Sopenharmony_ci{
18958c2ecf20Sopenharmony_ci	if (NULL == serial)
18968c2ecf20Sopenharmony_ci		return -EINVAL;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	return mux_device_request(serial,
18998c2ecf20Sopenharmony_ci				  USB_CDC_SEND_ENCAPSULATED_COMMAND,
19008c2ecf20Sopenharmony_ci				  serial->parent->port_spec & HSO_PORT_MASK,
19018c2ecf20Sopenharmony_ci				  serial->tx_urb,
19028c2ecf20Sopenharmony_ci				  &serial->ctrl_req_tx,
19038c2ecf20Sopenharmony_ci				  serial->tx_data, serial->tx_data_count);
19048c2ecf20Sopenharmony_ci}
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci/* write callback for Diag and CS port */
19078c2ecf20Sopenharmony_cistatic void hso_std_serial_write_bulk_callback(struct urb *urb)
19088c2ecf20Sopenharmony_ci{
19098c2ecf20Sopenharmony_ci	struct hso_serial *serial = urb->context;
19108c2ecf20Sopenharmony_ci	int status = urb->status;
19118c2ecf20Sopenharmony_ci	unsigned long flags;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	/* sanity check */
19148c2ecf20Sopenharmony_ci	if (!serial) {
19158c2ecf20Sopenharmony_ci		hso_dbg(0x1, "serial == NULL\n");
19168c2ecf20Sopenharmony_ci		return;
19178c2ecf20Sopenharmony_ci	}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
19208c2ecf20Sopenharmony_ci	serial->tx_urb_used = 0;
19218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
19228c2ecf20Sopenharmony_ci	if (status) {
19238c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, serial->parent);
19248c2ecf20Sopenharmony_ci		return;
19258c2ecf20Sopenharmony_ci	}
19268c2ecf20Sopenharmony_ci	hso_put_activity(serial->parent);
19278c2ecf20Sopenharmony_ci	tty_port_tty_wakeup(&serial->port);
19288c2ecf20Sopenharmony_ci	hso_kick_transmit(serial);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	hso_dbg(0x1, "\n");
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci/* called for writing diag or CS serial port */
19348c2ecf20Sopenharmony_cistatic int hso_std_serial_write_data(struct hso_serial *serial)
19358c2ecf20Sopenharmony_ci{
19368c2ecf20Sopenharmony_ci	int count = serial->tx_data_count;
19378c2ecf20Sopenharmony_ci	int result;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(serial->tx_urb,
19408c2ecf20Sopenharmony_ci			  serial->parent->usb,
19418c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(serial->parent->usb,
19428c2ecf20Sopenharmony_ci					  serial->out_endp->
19438c2ecf20Sopenharmony_ci					  bEndpointAddress & 0x7F),
19448c2ecf20Sopenharmony_ci			  serial->tx_data, serial->tx_data_count,
19458c2ecf20Sopenharmony_ci			  hso_std_serial_write_bulk_callback, serial);
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC);
19488c2ecf20Sopenharmony_ci	if (result) {
19498c2ecf20Sopenharmony_ci		dev_warn(&serial->parent->usb->dev,
19508c2ecf20Sopenharmony_ci			 "Failed to submit urb - res %d\n", result);
19518c2ecf20Sopenharmony_ci		return result;
19528c2ecf20Sopenharmony_ci	}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	return count;
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci/* callback after read or write on muxed serial port */
19588c2ecf20Sopenharmony_cistatic void ctrl_callback(struct urb *urb)
19598c2ecf20Sopenharmony_ci{
19608c2ecf20Sopenharmony_ci	struct hso_serial *serial = urb->context;
19618c2ecf20Sopenharmony_ci	struct usb_ctrlrequest *req;
19628c2ecf20Sopenharmony_ci	int status = urb->status;
19638c2ecf20Sopenharmony_ci	unsigned long flags;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	/* sanity check */
19668c2ecf20Sopenharmony_ci	if (!serial)
19678c2ecf20Sopenharmony_ci		return;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&serial->serial_lock, flags);
19708c2ecf20Sopenharmony_ci	serial->tx_urb_used = 0;
19718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&serial->serial_lock, flags);
19728c2ecf20Sopenharmony_ci	if (status) {
19738c2ecf20Sopenharmony_ci		handle_usb_error(status, __func__, serial->parent);
19748c2ecf20Sopenharmony_ci		return;
19758c2ecf20Sopenharmony_ci	}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	/* what request? */
19788c2ecf20Sopenharmony_ci	req = (struct usb_ctrlrequest *)(urb->setup_packet);
19798c2ecf20Sopenharmony_ci	hso_dbg(0x8, "--- Got muxed ctrl callback 0x%02X ---\n", status);
19808c2ecf20Sopenharmony_ci	hso_dbg(0x8, "Actual length of urb = %d\n", urb->actual_length);
19818c2ecf20Sopenharmony_ci	DUMP1(urb->transfer_buffer, urb->actual_length);
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	if (req->bRequestType ==
19848c2ecf20Sopenharmony_ci	    (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
19858c2ecf20Sopenharmony_ci		/* response to a read command */
19868c2ecf20Sopenharmony_ci		serial->rx_urb_filled[0] = 1;
19878c2ecf20Sopenharmony_ci		spin_lock_irqsave(&serial->serial_lock, flags);
19888c2ecf20Sopenharmony_ci		put_rxbuf_data_and_resubmit_ctrl_urb(serial);
19898c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&serial->serial_lock, flags);
19908c2ecf20Sopenharmony_ci	} else {
19918c2ecf20Sopenharmony_ci		hso_put_activity(serial->parent);
19928c2ecf20Sopenharmony_ci		tty_port_tty_wakeup(&serial->port);
19938c2ecf20Sopenharmony_ci		/* response to a write command */
19948c2ecf20Sopenharmony_ci		hso_kick_transmit(serial);
19958c2ecf20Sopenharmony_ci	}
19968c2ecf20Sopenharmony_ci}
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci/* handle RX data for serial port */
19998c2ecf20Sopenharmony_cistatic int put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	struct tty_struct *tty;
20028c2ecf20Sopenharmony_ci	int count;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	/* Sanity check */
20058c2ecf20Sopenharmony_ci	if (urb == NULL || serial == NULL) {
20068c2ecf20Sopenharmony_ci		hso_dbg(0x1, "serial = NULL\n");
20078c2ecf20Sopenharmony_ci		return -2;
20088c2ecf20Sopenharmony_ci	}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&serial->port);
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	if (tty && tty_throttled(tty)) {
20138c2ecf20Sopenharmony_ci		tty_kref_put(tty);
20148c2ecf20Sopenharmony_ci		return -1;
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	/* Push data to tty */
20188c2ecf20Sopenharmony_ci	hso_dbg(0x1, "data to push to tty\n");
20198c2ecf20Sopenharmony_ci	count = tty_buffer_request_room(&serial->port, urb->actual_length);
20208c2ecf20Sopenharmony_ci	if (count >= urb->actual_length) {
20218c2ecf20Sopenharmony_ci		tty_insert_flip_string(&serial->port, urb->transfer_buffer,
20228c2ecf20Sopenharmony_ci				       urb->actual_length);
20238c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&serial->port);
20248c2ecf20Sopenharmony_ci	} else {
20258c2ecf20Sopenharmony_ci		dev_warn(&serial->parent->usb->dev,
20268c2ecf20Sopenharmony_ci			 "dropping data, %d bytes lost\n", urb->actual_length);
20278c2ecf20Sopenharmony_ci	}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	tty_kref_put(tty);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci	return 0;
20348c2ecf20Sopenharmony_ci}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci/* Base driver functions */
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_cistatic void hso_log_port(struct hso_device *hso_dev)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	char *port_type;
20428c2ecf20Sopenharmony_ci	char port_dev[20];
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	switch (hso_dev->port_spec & HSO_PORT_MASK) {
20458c2ecf20Sopenharmony_ci	case HSO_PORT_CONTROL:
20468c2ecf20Sopenharmony_ci		port_type = "Control";
20478c2ecf20Sopenharmony_ci		break;
20488c2ecf20Sopenharmony_ci	case HSO_PORT_APP:
20498c2ecf20Sopenharmony_ci		port_type = "Application";
20508c2ecf20Sopenharmony_ci		break;
20518c2ecf20Sopenharmony_ci	case HSO_PORT_GPS:
20528c2ecf20Sopenharmony_ci		port_type = "GPS";
20538c2ecf20Sopenharmony_ci		break;
20548c2ecf20Sopenharmony_ci	case HSO_PORT_GPS_CONTROL:
20558c2ecf20Sopenharmony_ci		port_type = "GPS control";
20568c2ecf20Sopenharmony_ci		break;
20578c2ecf20Sopenharmony_ci	case HSO_PORT_APP2:
20588c2ecf20Sopenharmony_ci		port_type = "Application2";
20598c2ecf20Sopenharmony_ci		break;
20608c2ecf20Sopenharmony_ci	case HSO_PORT_PCSC:
20618c2ecf20Sopenharmony_ci		port_type = "PCSC";
20628c2ecf20Sopenharmony_ci		break;
20638c2ecf20Sopenharmony_ci	case HSO_PORT_DIAG:
20648c2ecf20Sopenharmony_ci		port_type = "Diagnostic";
20658c2ecf20Sopenharmony_ci		break;
20668c2ecf20Sopenharmony_ci	case HSO_PORT_DIAG2:
20678c2ecf20Sopenharmony_ci		port_type = "Diagnostic2";
20688c2ecf20Sopenharmony_ci		break;
20698c2ecf20Sopenharmony_ci	case HSO_PORT_MODEM:
20708c2ecf20Sopenharmony_ci		port_type = "Modem";
20718c2ecf20Sopenharmony_ci		break;
20728c2ecf20Sopenharmony_ci	case HSO_PORT_NETWORK:
20738c2ecf20Sopenharmony_ci		port_type = "Network";
20748c2ecf20Sopenharmony_ci		break;
20758c2ecf20Sopenharmony_ci	default:
20768c2ecf20Sopenharmony_ci		port_type = "Unknown";
20778c2ecf20Sopenharmony_ci		break;
20788c2ecf20Sopenharmony_ci	}
20798c2ecf20Sopenharmony_ci	if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
20808c2ecf20Sopenharmony_ci		sprintf(port_dev, "%s", dev2net(hso_dev)->net->name);
20818c2ecf20Sopenharmony_ci	} else
20828c2ecf20Sopenharmony_ci		sprintf(port_dev, "/dev/%s%d", tty_filename,
20838c2ecf20Sopenharmony_ci			dev2ser(hso_dev)->minor);
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n",
20868c2ecf20Sopenharmony_ci		port_type, port_dev);
20878c2ecf20Sopenharmony_ci}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_cistatic int hso_start_net_device(struct hso_device *hso_dev)
20908c2ecf20Sopenharmony_ci{
20918c2ecf20Sopenharmony_ci	int i, result = 0;
20928c2ecf20Sopenharmony_ci	struct hso_net *hso_net = dev2net(hso_dev);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	if (!hso_net)
20958c2ecf20Sopenharmony_ci		return -ENODEV;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	/* send URBs for all read buffers */
20988c2ecf20Sopenharmony_ci	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci		/* Prep a receive URB */
21018c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i],
21028c2ecf20Sopenharmony_ci				  hso_dev->usb,
21038c2ecf20Sopenharmony_ci				  usb_rcvbulkpipe(hso_dev->usb,
21048c2ecf20Sopenharmony_ci						  hso_net->in_endp->
21058c2ecf20Sopenharmony_ci						  bEndpointAddress & 0x7F),
21068c2ecf20Sopenharmony_ci				  hso_net->mux_bulk_rx_buf_pool[i],
21078c2ecf20Sopenharmony_ci				  MUX_BULK_RX_BUF_SIZE, read_bulk_callback,
21088c2ecf20Sopenharmony_ci				  hso_net);
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci		/* Put it out there so the device can send us stuff */
21118c2ecf20Sopenharmony_ci		result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i],
21128c2ecf20Sopenharmony_ci					GFP_NOIO);
21138c2ecf20Sopenharmony_ci		if (result)
21148c2ecf20Sopenharmony_ci			dev_warn(&hso_dev->usb->dev,
21158c2ecf20Sopenharmony_ci				"%s failed mux_bulk_rx_urb[%d] %d\n", __func__,
21168c2ecf20Sopenharmony_ci				i, result);
21178c2ecf20Sopenharmony_ci	}
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	return result;
21208c2ecf20Sopenharmony_ci}
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_cistatic int hso_stop_net_device(struct hso_device *hso_dev)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	int i;
21258c2ecf20Sopenharmony_ci	struct hso_net *hso_net = dev2net(hso_dev);
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (!hso_net)
21288c2ecf20Sopenharmony_ci		return -ENODEV;
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
21318c2ecf20Sopenharmony_ci		if (hso_net->mux_bulk_rx_urb_pool[i])
21328c2ecf20Sopenharmony_ci			usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]);
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	}
21358c2ecf20Sopenharmony_ci	if (hso_net->mux_bulk_tx_urb)
21368c2ecf20Sopenharmony_ci		usb_kill_urb(hso_net->mux_bulk_tx_urb);
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci	return 0;
21398c2ecf20Sopenharmony_ci}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_cistatic int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags)
21428c2ecf20Sopenharmony_ci{
21438c2ecf20Sopenharmony_ci	int i, result = 0;
21448c2ecf20Sopenharmony_ci	struct hso_serial *serial = dev2ser(hso_dev);
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	if (!serial)
21478c2ecf20Sopenharmony_ci		return -ENODEV;
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	/* If it is not the MUX port fill in and submit a bulk urb (already
21508c2ecf20Sopenharmony_ci	 * allocated in hso_serial_start) */
21518c2ecf20Sopenharmony_ci	if (!(serial->parent->port_spec & HSO_INTF_MUX)) {
21528c2ecf20Sopenharmony_ci		for (i = 0; i < serial->num_rx_urbs; i++) {
21538c2ecf20Sopenharmony_ci			usb_fill_bulk_urb(serial->rx_urb[i],
21548c2ecf20Sopenharmony_ci					  serial->parent->usb,
21558c2ecf20Sopenharmony_ci					  usb_rcvbulkpipe(serial->parent->usb,
21568c2ecf20Sopenharmony_ci							  serial->in_endp->
21578c2ecf20Sopenharmony_ci							  bEndpointAddress &
21588c2ecf20Sopenharmony_ci							  0x7F),
21598c2ecf20Sopenharmony_ci					  serial->rx_data[i],
21608c2ecf20Sopenharmony_ci					  serial->rx_data_length,
21618c2ecf20Sopenharmony_ci					  hso_std_serial_read_bulk_callback,
21628c2ecf20Sopenharmony_ci					  serial);
21638c2ecf20Sopenharmony_ci			result = usb_submit_urb(serial->rx_urb[i], flags);
21648c2ecf20Sopenharmony_ci			if (result) {
21658c2ecf20Sopenharmony_ci				dev_warn(&serial->parent->usb->dev,
21668c2ecf20Sopenharmony_ci					 "Failed to submit urb - res %d\n",
21678c2ecf20Sopenharmony_ci					 result);
21688c2ecf20Sopenharmony_ci				break;
21698c2ecf20Sopenharmony_ci			}
21708c2ecf20Sopenharmony_ci		}
21718c2ecf20Sopenharmony_ci	} else {
21728c2ecf20Sopenharmony_ci		mutex_lock(&serial->shared_int->shared_int_lock);
21738c2ecf20Sopenharmony_ci		if (!serial->shared_int->use_count) {
21748c2ecf20Sopenharmony_ci			result =
21758c2ecf20Sopenharmony_ci			    hso_mux_submit_intr_urb(serial->shared_int,
21768c2ecf20Sopenharmony_ci						    hso_dev->usb, flags);
21778c2ecf20Sopenharmony_ci		}
21788c2ecf20Sopenharmony_ci		serial->shared_int->use_count++;
21798c2ecf20Sopenharmony_ci		mutex_unlock(&serial->shared_int->shared_int_lock);
21808c2ecf20Sopenharmony_ci	}
21818c2ecf20Sopenharmony_ci	if (serial->tiocmget)
21828c2ecf20Sopenharmony_ci		tiocmget_submit_urb(serial,
21838c2ecf20Sopenharmony_ci				    serial->tiocmget,
21848c2ecf20Sopenharmony_ci				    serial->parent->usb);
21858c2ecf20Sopenharmony_ci	return result;
21868c2ecf20Sopenharmony_ci}
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_cistatic int hso_stop_serial_device(struct hso_device *hso_dev)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	int i;
21918c2ecf20Sopenharmony_ci	struct hso_serial *serial = dev2ser(hso_dev);
21928c2ecf20Sopenharmony_ci	struct hso_tiocmget  *tiocmget;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (!serial)
21958c2ecf20Sopenharmony_ci		return -ENODEV;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	for (i = 0; i < serial->num_rx_urbs; i++) {
21988c2ecf20Sopenharmony_ci		if (serial->rx_urb[i]) {
21998c2ecf20Sopenharmony_ci			usb_kill_urb(serial->rx_urb[i]);
22008c2ecf20Sopenharmony_ci			serial->rx_urb_filled[i] = 0;
22018c2ecf20Sopenharmony_ci		}
22028c2ecf20Sopenharmony_ci	}
22038c2ecf20Sopenharmony_ci	serial->curr_rx_urb_idx = 0;
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	if (serial->tx_urb)
22068c2ecf20Sopenharmony_ci		usb_kill_urb(serial->tx_urb);
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	if (serial->shared_int) {
22098c2ecf20Sopenharmony_ci		mutex_lock(&serial->shared_int->shared_int_lock);
22108c2ecf20Sopenharmony_ci		if (serial->shared_int->use_count &&
22118c2ecf20Sopenharmony_ci		    (--serial->shared_int->use_count == 0)) {
22128c2ecf20Sopenharmony_ci			struct urb *urb;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci			urb = serial->shared_int->shared_intr_urb;
22158c2ecf20Sopenharmony_ci			if (urb)
22168c2ecf20Sopenharmony_ci				usb_kill_urb(urb);
22178c2ecf20Sopenharmony_ci		}
22188c2ecf20Sopenharmony_ci		mutex_unlock(&serial->shared_int->shared_int_lock);
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci	tiocmget = serial->tiocmget;
22218c2ecf20Sopenharmony_ci	if (tiocmget) {
22228c2ecf20Sopenharmony_ci		wake_up_interruptible(&tiocmget->waitq);
22238c2ecf20Sopenharmony_ci		usb_kill_urb(tiocmget->urb);
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	return 0;
22278c2ecf20Sopenharmony_ci}
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_cistatic void hso_serial_tty_unregister(struct hso_serial *serial)
22308c2ecf20Sopenharmony_ci{
22318c2ecf20Sopenharmony_ci	tty_unregister_device(tty_drv, serial->minor);
22328c2ecf20Sopenharmony_ci	release_minor(serial);
22338c2ecf20Sopenharmony_ci}
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_cistatic void hso_serial_common_free(struct hso_serial *serial)
22368c2ecf20Sopenharmony_ci{
22378c2ecf20Sopenharmony_ci	int i;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	for (i = 0; i < serial->num_rx_urbs; i++) {
22408c2ecf20Sopenharmony_ci		/* unlink and free RX URB */
22418c2ecf20Sopenharmony_ci		usb_free_urb(serial->rx_urb[i]);
22428c2ecf20Sopenharmony_ci		/* free the RX buffer */
22438c2ecf20Sopenharmony_ci		kfree(serial->rx_data[i]);
22448c2ecf20Sopenharmony_ci	}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci	/* unlink and free TX URB */
22478c2ecf20Sopenharmony_ci	usb_free_urb(serial->tx_urb);
22488c2ecf20Sopenharmony_ci	kfree(serial->tx_buffer);
22498c2ecf20Sopenharmony_ci	kfree(serial->tx_data);
22508c2ecf20Sopenharmony_ci	tty_port_destroy(&serial->port);
22518c2ecf20Sopenharmony_ci}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_cistatic int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
22548c2ecf20Sopenharmony_ci				    int rx_size, int tx_size)
22558c2ecf20Sopenharmony_ci{
22568c2ecf20Sopenharmony_ci	int i;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	tty_port_init(&serial->port);
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	if (obtain_minor(serial))
22618c2ecf20Sopenharmony_ci		goto exit2;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	/* register our minor number */
22648c2ecf20Sopenharmony_ci	serial->parent->dev = tty_port_register_device_attr(&serial->port,
22658c2ecf20Sopenharmony_ci			tty_drv, serial->minor, &serial->parent->interface->dev,
22668c2ecf20Sopenharmony_ci			serial->parent, hso_serial_dev_groups);
22678c2ecf20Sopenharmony_ci	if (IS_ERR(serial->parent->dev)) {
22688c2ecf20Sopenharmony_ci		release_minor(serial);
22698c2ecf20Sopenharmony_ci		goto exit2;
22708c2ecf20Sopenharmony_ci	}
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci	serial->magic = HSO_SERIAL_MAGIC;
22738c2ecf20Sopenharmony_ci	spin_lock_init(&serial->serial_lock);
22748c2ecf20Sopenharmony_ci	serial->num_rx_urbs = num_urbs;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	/* RX, allocate urb and initialize */
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	/* prepare our RX buffer */
22798c2ecf20Sopenharmony_ci	serial->rx_data_length = rx_size;
22808c2ecf20Sopenharmony_ci	for (i = 0; i < serial->num_rx_urbs; i++) {
22818c2ecf20Sopenharmony_ci		serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
22828c2ecf20Sopenharmony_ci		if (!serial->rx_urb[i])
22838c2ecf20Sopenharmony_ci			goto exit;
22848c2ecf20Sopenharmony_ci		serial->rx_urb[i]->transfer_buffer = NULL;
22858c2ecf20Sopenharmony_ci		serial->rx_urb[i]->transfer_buffer_length = 0;
22868c2ecf20Sopenharmony_ci		serial->rx_data[i] = kzalloc(serial->rx_data_length,
22878c2ecf20Sopenharmony_ci					     GFP_KERNEL);
22888c2ecf20Sopenharmony_ci		if (!serial->rx_data[i])
22898c2ecf20Sopenharmony_ci			goto exit;
22908c2ecf20Sopenharmony_ci	}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	/* TX, allocate urb and initialize */
22938c2ecf20Sopenharmony_ci	serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
22948c2ecf20Sopenharmony_ci	if (!serial->tx_urb)
22958c2ecf20Sopenharmony_ci		goto exit;
22968c2ecf20Sopenharmony_ci	serial->tx_urb->transfer_buffer = NULL;
22978c2ecf20Sopenharmony_ci	serial->tx_urb->transfer_buffer_length = 0;
22988c2ecf20Sopenharmony_ci	/* prepare our TX buffer */
22998c2ecf20Sopenharmony_ci	serial->tx_data_count = 0;
23008c2ecf20Sopenharmony_ci	serial->tx_buffer_count = 0;
23018c2ecf20Sopenharmony_ci	serial->tx_data_length = tx_size;
23028c2ecf20Sopenharmony_ci	serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL);
23038c2ecf20Sopenharmony_ci	if (!serial->tx_data)
23048c2ecf20Sopenharmony_ci		goto exit;
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL);
23078c2ecf20Sopenharmony_ci	if (!serial->tx_buffer)
23088c2ecf20Sopenharmony_ci		goto exit;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	return 0;
23118c2ecf20Sopenharmony_ciexit:
23128c2ecf20Sopenharmony_ci	hso_serial_tty_unregister(serial);
23138c2ecf20Sopenharmony_ciexit2:
23148c2ecf20Sopenharmony_ci	hso_serial_common_free(serial);
23158c2ecf20Sopenharmony_ci	return -1;
23168c2ecf20Sopenharmony_ci}
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci/* Creates a general hso device */
23198c2ecf20Sopenharmony_cistatic struct hso_device *hso_create_device(struct usb_interface *intf,
23208c2ecf20Sopenharmony_ci					    int port_spec)
23218c2ecf20Sopenharmony_ci{
23228c2ecf20Sopenharmony_ci	struct hso_device *hso_dev;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC);
23258c2ecf20Sopenharmony_ci	if (!hso_dev)
23268c2ecf20Sopenharmony_ci		return NULL;
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	hso_dev->port_spec = port_spec;
23298c2ecf20Sopenharmony_ci	hso_dev->usb = interface_to_usbdev(intf);
23308c2ecf20Sopenharmony_ci	hso_dev->interface = intf;
23318c2ecf20Sopenharmony_ci	kref_init(&hso_dev->ref);
23328c2ecf20Sopenharmony_ci	mutex_init(&hso_dev->mutex);
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
23358c2ecf20Sopenharmony_ci	INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	return hso_dev;
23388c2ecf20Sopenharmony_ci}
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci/* Removes a network device in the network device table */
23418c2ecf20Sopenharmony_cistatic int remove_net_device(struct hso_device *hso_dev)
23428c2ecf20Sopenharmony_ci{
23438c2ecf20Sopenharmony_ci	int i;
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
23468c2ecf20Sopenharmony_ci		if (network_table[i] == hso_dev) {
23478c2ecf20Sopenharmony_ci			network_table[i] = NULL;
23488c2ecf20Sopenharmony_ci			break;
23498c2ecf20Sopenharmony_ci		}
23508c2ecf20Sopenharmony_ci	}
23518c2ecf20Sopenharmony_ci	if (i == HSO_MAX_NET_DEVICES)
23528c2ecf20Sopenharmony_ci		return -1;
23538c2ecf20Sopenharmony_ci	return 0;
23548c2ecf20Sopenharmony_ci}
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci/* Frees our network device */
23578c2ecf20Sopenharmony_cistatic void hso_free_net_device(struct hso_device *hso_dev)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	int i;
23608c2ecf20Sopenharmony_ci	struct hso_net *hso_net = dev2net(hso_dev);
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	if (!hso_net)
23638c2ecf20Sopenharmony_ci		return;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	remove_net_device(hso_net->parent);
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	if (hso_net->net)
23688c2ecf20Sopenharmony_ci		unregister_netdev(hso_net->net);
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	/* start freeing */
23718c2ecf20Sopenharmony_ci	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
23728c2ecf20Sopenharmony_ci		usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
23738c2ecf20Sopenharmony_ci		kfree(hso_net->mux_bulk_rx_buf_pool[i]);
23748c2ecf20Sopenharmony_ci		hso_net->mux_bulk_rx_buf_pool[i] = NULL;
23758c2ecf20Sopenharmony_ci	}
23768c2ecf20Sopenharmony_ci	usb_free_urb(hso_net->mux_bulk_tx_urb);
23778c2ecf20Sopenharmony_ci	kfree(hso_net->mux_bulk_tx_buf);
23788c2ecf20Sopenharmony_ci	hso_net->mux_bulk_tx_buf = NULL;
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci	if (hso_net->net)
23818c2ecf20Sopenharmony_ci		free_netdev(hso_net->net);
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	kfree(hso_dev);
23848c2ecf20Sopenharmony_ci}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_cistatic const struct net_device_ops hso_netdev_ops = {
23878c2ecf20Sopenharmony_ci	.ndo_open	= hso_net_open,
23888c2ecf20Sopenharmony_ci	.ndo_stop	= hso_net_close,
23898c2ecf20Sopenharmony_ci	.ndo_start_xmit = hso_net_start_xmit,
23908c2ecf20Sopenharmony_ci	.ndo_tx_timeout = hso_net_tx_timeout,
23918c2ecf20Sopenharmony_ci};
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci/* initialize the network interface */
23948c2ecf20Sopenharmony_cistatic void hso_net_init(struct net_device *net)
23958c2ecf20Sopenharmony_ci{
23968c2ecf20Sopenharmony_ci	struct hso_net *hso_net = netdev_priv(net);
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	hso_dbg(0x1, "sizeof hso_net is %zu\n", sizeof(*hso_net));
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	/* fill in the other fields */
24018c2ecf20Sopenharmony_ci	net->netdev_ops = &hso_netdev_ops;
24028c2ecf20Sopenharmony_ci	net->watchdog_timeo = HSO_NET_TX_TIMEOUT;
24038c2ecf20Sopenharmony_ci	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
24048c2ecf20Sopenharmony_ci	net->type = ARPHRD_NONE;
24058c2ecf20Sopenharmony_ci	net->mtu = DEFAULT_MTU - 14;
24068c2ecf20Sopenharmony_ci	net->tx_queue_len = 10;
24078c2ecf20Sopenharmony_ci	net->ethtool_ops = &ops;
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	/* and initialize the semaphore */
24108c2ecf20Sopenharmony_ci	spin_lock_init(&hso_net->net_lock);
24118c2ecf20Sopenharmony_ci}
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci/* Adds a network device in the network device table */
24148c2ecf20Sopenharmony_cistatic int add_net_device(struct hso_device *hso_dev)
24158c2ecf20Sopenharmony_ci{
24168c2ecf20Sopenharmony_ci	int i;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
24198c2ecf20Sopenharmony_ci		if (network_table[i] == NULL) {
24208c2ecf20Sopenharmony_ci			network_table[i] = hso_dev;
24218c2ecf20Sopenharmony_ci			break;
24228c2ecf20Sopenharmony_ci		}
24238c2ecf20Sopenharmony_ci	}
24248c2ecf20Sopenharmony_ci	if (i == HSO_MAX_NET_DEVICES)
24258c2ecf20Sopenharmony_ci		return -1;
24268c2ecf20Sopenharmony_ci	return 0;
24278c2ecf20Sopenharmony_ci}
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_cistatic int hso_rfkill_set_block(void *data, bool blocked)
24308c2ecf20Sopenharmony_ci{
24318c2ecf20Sopenharmony_ci	struct hso_device *hso_dev = data;
24328c2ecf20Sopenharmony_ci	int enabled = !blocked;
24338c2ecf20Sopenharmony_ci	int rv;
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	mutex_lock(&hso_dev->mutex);
24368c2ecf20Sopenharmony_ci	if (hso_dev->usb_gone)
24378c2ecf20Sopenharmony_ci		rv = 0;
24388c2ecf20Sopenharmony_ci	else
24398c2ecf20Sopenharmony_ci		rv = usb_control_msg(hso_dev->usb, usb_sndctrlpipe(hso_dev->usb, 0),
24408c2ecf20Sopenharmony_ci				       enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0,
24418c2ecf20Sopenharmony_ci				       USB_CTRL_SET_TIMEOUT);
24428c2ecf20Sopenharmony_ci	mutex_unlock(&hso_dev->mutex);
24438c2ecf20Sopenharmony_ci	return rv;
24448c2ecf20Sopenharmony_ci}
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_cistatic const struct rfkill_ops hso_rfkill_ops = {
24478c2ecf20Sopenharmony_ci	.set_block = hso_rfkill_set_block,
24488c2ecf20Sopenharmony_ci};
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci/* Creates and sets up everything for rfkill */
24518c2ecf20Sopenharmony_cistatic void hso_create_rfkill(struct hso_device *hso_dev,
24528c2ecf20Sopenharmony_ci			     struct usb_interface *interface)
24538c2ecf20Sopenharmony_ci{
24548c2ecf20Sopenharmony_ci	struct hso_net *hso_net = dev2net(hso_dev);
24558c2ecf20Sopenharmony_ci	struct device *dev = &hso_net->net->dev;
24568c2ecf20Sopenharmony_ci	static u32 rfkill_counter;
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d",
24598c2ecf20Sopenharmony_ci		 rfkill_counter++);
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci	hso_net->rfkill = rfkill_alloc(hso_net->name,
24628c2ecf20Sopenharmony_ci				       &interface_to_usbdev(interface)->dev,
24638c2ecf20Sopenharmony_ci				       RFKILL_TYPE_WWAN,
24648c2ecf20Sopenharmony_ci				       &hso_rfkill_ops, hso_dev);
24658c2ecf20Sopenharmony_ci	if (!hso_net->rfkill)
24668c2ecf20Sopenharmony_ci		return;
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	if (rfkill_register(hso_net->rfkill) < 0) {
24698c2ecf20Sopenharmony_ci		rfkill_destroy(hso_net->rfkill);
24708c2ecf20Sopenharmony_ci		hso_net->rfkill = NULL;
24718c2ecf20Sopenharmony_ci		dev_err(dev, "%s - Failed to register rfkill\n", __func__);
24728c2ecf20Sopenharmony_ci		return;
24738c2ecf20Sopenharmony_ci	}
24748c2ecf20Sopenharmony_ci}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_cistatic struct device_type hso_type = {
24778c2ecf20Sopenharmony_ci	.name	= "wwan",
24788c2ecf20Sopenharmony_ci};
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_ci/* Creates our network device */
24818c2ecf20Sopenharmony_cistatic struct hso_device *hso_create_net_device(struct usb_interface *interface,
24828c2ecf20Sopenharmony_ci						int port_spec)
24838c2ecf20Sopenharmony_ci{
24848c2ecf20Sopenharmony_ci	int result, i;
24858c2ecf20Sopenharmony_ci	struct net_device *net;
24868c2ecf20Sopenharmony_ci	struct hso_net *hso_net;
24878c2ecf20Sopenharmony_ci	struct hso_device *hso_dev;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	hso_dev = hso_create_device(interface, port_spec);
24908c2ecf20Sopenharmony_ci	if (!hso_dev)
24918c2ecf20Sopenharmony_ci		return NULL;
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	/* allocate our network device, then we can put in our private data */
24948c2ecf20Sopenharmony_ci	/* call hso_net_init to do the basic initialization */
24958c2ecf20Sopenharmony_ci	net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN,
24968c2ecf20Sopenharmony_ci			   hso_net_init);
24978c2ecf20Sopenharmony_ci	if (!net) {
24988c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Unable to create ethernet device\n");
24998c2ecf20Sopenharmony_ci		goto err_hso_dev;
25008c2ecf20Sopenharmony_ci	}
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	hso_net = netdev_priv(net);
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	hso_dev->port_data.dev_net = hso_net;
25058c2ecf20Sopenharmony_ci	hso_net->net = net;
25068c2ecf20Sopenharmony_ci	hso_net->parent = hso_dev;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
25098c2ecf20Sopenharmony_ci				      USB_DIR_IN);
25108c2ecf20Sopenharmony_ci	if (!hso_net->in_endp) {
25118c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Can't find BULK IN endpoint\n");
25128c2ecf20Sopenharmony_ci		goto err_net;
25138c2ecf20Sopenharmony_ci	}
25148c2ecf20Sopenharmony_ci	hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
25158c2ecf20Sopenharmony_ci				       USB_DIR_OUT);
25168c2ecf20Sopenharmony_ci	if (!hso_net->out_endp) {
25178c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Can't find BULK OUT endpoint\n");
25188c2ecf20Sopenharmony_ci		goto err_net;
25198c2ecf20Sopenharmony_ci	}
25208c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(net, &interface->dev);
25218c2ecf20Sopenharmony_ci	SET_NETDEV_DEVTYPE(net, &hso_type);
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	/* start allocating */
25248c2ecf20Sopenharmony_ci	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
25258c2ecf20Sopenharmony_ci		hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL);
25268c2ecf20Sopenharmony_ci		if (!hso_net->mux_bulk_rx_urb_pool[i])
25278c2ecf20Sopenharmony_ci			goto err_mux_bulk_rx;
25288c2ecf20Sopenharmony_ci		hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE,
25298c2ecf20Sopenharmony_ci							   GFP_KERNEL);
25308c2ecf20Sopenharmony_ci		if (!hso_net->mux_bulk_rx_buf_pool[i])
25318c2ecf20Sopenharmony_ci			goto err_mux_bulk_rx;
25328c2ecf20Sopenharmony_ci	}
25338c2ecf20Sopenharmony_ci	hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL);
25348c2ecf20Sopenharmony_ci	if (!hso_net->mux_bulk_tx_urb)
25358c2ecf20Sopenharmony_ci		goto err_mux_bulk_rx;
25368c2ecf20Sopenharmony_ci	hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL);
25378c2ecf20Sopenharmony_ci	if (!hso_net->mux_bulk_tx_buf)
25388c2ecf20Sopenharmony_ci		goto err_free_tx_urb;
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ci	result = add_net_device(hso_dev);
25418c2ecf20Sopenharmony_ci	if (result) {
25428c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Failed to add net device\n");
25438c2ecf20Sopenharmony_ci		goto err_free_tx_buf;
25448c2ecf20Sopenharmony_ci	}
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	/* registering our net device */
25478c2ecf20Sopenharmony_ci	result = register_netdev(net);
25488c2ecf20Sopenharmony_ci	if (result) {
25498c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Failed to register device\n");
25508c2ecf20Sopenharmony_ci		goto err_rmv_ndev;
25518c2ecf20Sopenharmony_ci	}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	hso_log_port(hso_dev);
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	hso_create_rfkill(hso_dev, interface);
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	return hso_dev;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_cierr_rmv_ndev:
25608c2ecf20Sopenharmony_ci	remove_net_device(hso_dev);
25618c2ecf20Sopenharmony_cierr_free_tx_buf:
25628c2ecf20Sopenharmony_ci	kfree(hso_net->mux_bulk_tx_buf);
25638c2ecf20Sopenharmony_cierr_free_tx_urb:
25648c2ecf20Sopenharmony_ci	usb_free_urb(hso_net->mux_bulk_tx_urb);
25658c2ecf20Sopenharmony_cierr_mux_bulk_rx:
25668c2ecf20Sopenharmony_ci	for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
25678c2ecf20Sopenharmony_ci		usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
25688c2ecf20Sopenharmony_ci		kfree(hso_net->mux_bulk_rx_buf_pool[i]);
25698c2ecf20Sopenharmony_ci	}
25708c2ecf20Sopenharmony_cierr_net:
25718c2ecf20Sopenharmony_ci	free_netdev(net);
25728c2ecf20Sopenharmony_cierr_hso_dev:
25738c2ecf20Sopenharmony_ci	kfree(hso_dev);
25748c2ecf20Sopenharmony_ci	return NULL;
25758c2ecf20Sopenharmony_ci}
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_cistatic void hso_free_tiomget(struct hso_serial *serial)
25788c2ecf20Sopenharmony_ci{
25798c2ecf20Sopenharmony_ci	struct hso_tiocmget *tiocmget;
25808c2ecf20Sopenharmony_ci	if (!serial)
25818c2ecf20Sopenharmony_ci		return;
25828c2ecf20Sopenharmony_ci	tiocmget = serial->tiocmget;
25838c2ecf20Sopenharmony_ci	if (tiocmget) {
25848c2ecf20Sopenharmony_ci		usb_free_urb(tiocmget->urb);
25858c2ecf20Sopenharmony_ci		tiocmget->urb = NULL;
25868c2ecf20Sopenharmony_ci		serial->tiocmget = NULL;
25878c2ecf20Sopenharmony_ci		kfree(tiocmget->serial_state_notification);
25888c2ecf20Sopenharmony_ci		tiocmget->serial_state_notification = NULL;
25898c2ecf20Sopenharmony_ci		kfree(tiocmget);
25908c2ecf20Sopenharmony_ci	}
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci/* Frees an AT channel ( goes for both mux and non-mux ) */
25948c2ecf20Sopenharmony_cistatic void hso_free_serial_device(struct hso_device *hso_dev)
25958c2ecf20Sopenharmony_ci{
25968c2ecf20Sopenharmony_ci	struct hso_serial *serial = dev2ser(hso_dev);
25978c2ecf20Sopenharmony_ci
25988c2ecf20Sopenharmony_ci	if (!serial)
25998c2ecf20Sopenharmony_ci		return;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	hso_serial_common_free(serial);
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci	if (serial->shared_int) {
26048c2ecf20Sopenharmony_ci		mutex_lock(&serial->shared_int->shared_int_lock);
26058c2ecf20Sopenharmony_ci		if (--serial->shared_int->ref_count == 0)
26068c2ecf20Sopenharmony_ci			hso_free_shared_int(serial->shared_int);
26078c2ecf20Sopenharmony_ci		else
26088c2ecf20Sopenharmony_ci			mutex_unlock(&serial->shared_int->shared_int_lock);
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ci	hso_free_tiomget(serial);
26118c2ecf20Sopenharmony_ci	kfree(serial);
26128c2ecf20Sopenharmony_ci	kfree(hso_dev);
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci/* Creates a bulk AT channel */
26168c2ecf20Sopenharmony_cistatic struct hso_device *hso_create_bulk_serial_device(
26178c2ecf20Sopenharmony_ci			struct usb_interface *interface, int port)
26188c2ecf20Sopenharmony_ci{
26198c2ecf20Sopenharmony_ci	struct hso_device *hso_dev;
26208c2ecf20Sopenharmony_ci	struct hso_serial *serial;
26218c2ecf20Sopenharmony_ci	int num_urbs;
26228c2ecf20Sopenharmony_ci	struct hso_tiocmget *tiocmget;
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	hso_dev = hso_create_device(interface, port);
26258c2ecf20Sopenharmony_ci	if (!hso_dev)
26268c2ecf20Sopenharmony_ci		return NULL;
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
26298c2ecf20Sopenharmony_ci	if (!serial)
26308c2ecf20Sopenharmony_ci		goto exit;
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci	serial->parent = hso_dev;
26338c2ecf20Sopenharmony_ci	hso_dev->port_data.dev_serial = serial;
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
26368c2ecf20Sopenharmony_ci		num_urbs = 2;
26378c2ecf20Sopenharmony_ci		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
26388c2ecf20Sopenharmony_ci					   GFP_KERNEL);
26398c2ecf20Sopenharmony_ci		if (!serial->tiocmget)
26408c2ecf20Sopenharmony_ci			goto exit;
26418c2ecf20Sopenharmony_ci		serial->tiocmget->serial_state_notification
26428c2ecf20Sopenharmony_ci			= kzalloc(sizeof(struct hso_serial_state_notification),
26438c2ecf20Sopenharmony_ci					   GFP_KERNEL);
26448c2ecf20Sopenharmony_ci		if (!serial->tiocmget->serial_state_notification)
26458c2ecf20Sopenharmony_ci			goto exit;
26468c2ecf20Sopenharmony_ci		tiocmget = serial->tiocmget;
26478c2ecf20Sopenharmony_ci		tiocmget->endp = hso_get_ep(interface,
26488c2ecf20Sopenharmony_ci					    USB_ENDPOINT_XFER_INT,
26498c2ecf20Sopenharmony_ci					    USB_DIR_IN);
26508c2ecf20Sopenharmony_ci		if (!tiocmget->endp) {
26518c2ecf20Sopenharmony_ci			dev_err(&interface->dev, "Failed to find INT IN ep\n");
26528c2ecf20Sopenharmony_ci			goto exit;
26538c2ecf20Sopenharmony_ci		}
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci		tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
26568c2ecf20Sopenharmony_ci		if (!tiocmget->urb)
26578c2ecf20Sopenharmony_ci			goto exit;
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci		mutex_init(&tiocmget->mutex);
26608c2ecf20Sopenharmony_ci		init_waitqueue_head(&tiocmget->waitq);
26618c2ecf20Sopenharmony_ci	} else {
26628c2ecf20Sopenharmony_ci		num_urbs = 1;
26638c2ecf20Sopenharmony_ci	}
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE,
26668c2ecf20Sopenharmony_ci				     BULK_URB_TX_SIZE))
26678c2ecf20Sopenharmony_ci		goto exit;
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci	serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
26708c2ecf20Sopenharmony_ci				     USB_DIR_IN);
26718c2ecf20Sopenharmony_ci	if (!serial->in_endp) {
26728c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Failed to find BULK IN ep\n");
26738c2ecf20Sopenharmony_ci		goto exit2;
26748c2ecf20Sopenharmony_ci	}
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	if (!
26778c2ecf20Sopenharmony_ci	    (serial->out_endp =
26788c2ecf20Sopenharmony_ci	     hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) {
26798c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Failed to find BULK OUT ep\n");
26808c2ecf20Sopenharmony_ci		goto exit2;
26818c2ecf20Sopenharmony_ci	}
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci	serial->write_data = hso_std_serial_write_data;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	/* setup the proc dirs and files if needed */
26868c2ecf20Sopenharmony_ci	hso_log_port(hso_dev);
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci	/* done, return it */
26898c2ecf20Sopenharmony_ci	return hso_dev;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ciexit2:
26928c2ecf20Sopenharmony_ci	hso_serial_tty_unregister(serial);
26938c2ecf20Sopenharmony_ci	hso_serial_common_free(serial);
26948c2ecf20Sopenharmony_ciexit:
26958c2ecf20Sopenharmony_ci	hso_free_tiomget(serial);
26968c2ecf20Sopenharmony_ci	kfree(serial);
26978c2ecf20Sopenharmony_ci	kfree(hso_dev);
26988c2ecf20Sopenharmony_ci	return NULL;
26998c2ecf20Sopenharmony_ci}
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci/* Creates a multiplexed AT channel */
27028c2ecf20Sopenharmony_cistatic
27038c2ecf20Sopenharmony_cistruct hso_device *hso_create_mux_serial_device(struct usb_interface *interface,
27048c2ecf20Sopenharmony_ci						int port,
27058c2ecf20Sopenharmony_ci						struct hso_shared_int *mux)
27068c2ecf20Sopenharmony_ci{
27078c2ecf20Sopenharmony_ci	struct hso_device *hso_dev;
27088c2ecf20Sopenharmony_ci	struct hso_serial *serial;
27098c2ecf20Sopenharmony_ci	int port_spec;
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	port_spec = HSO_INTF_MUX;
27128c2ecf20Sopenharmony_ci	port_spec &= ~HSO_PORT_MASK;
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	port_spec |= hso_mux_to_port(port);
27158c2ecf20Sopenharmony_ci	if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT)
27168c2ecf20Sopenharmony_ci		return NULL;
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci	hso_dev = hso_create_device(interface, port_spec);
27198c2ecf20Sopenharmony_ci	if (!hso_dev)
27208c2ecf20Sopenharmony_ci		return NULL;
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	serial = kzalloc(sizeof(*serial), GFP_KERNEL);
27238c2ecf20Sopenharmony_ci	if (!serial)
27248c2ecf20Sopenharmony_ci		goto err_free_dev;
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci	hso_dev->port_data.dev_serial = serial;
27278c2ecf20Sopenharmony_ci	serial->parent = hso_dev;
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	if (hso_serial_common_create
27308c2ecf20Sopenharmony_ci	    (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE))
27318c2ecf20Sopenharmony_ci		goto err_free_serial;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	serial->tx_data_length--;
27348c2ecf20Sopenharmony_ci	serial->write_data = hso_mux_serial_write_data;
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	serial->shared_int = mux;
27378c2ecf20Sopenharmony_ci	mutex_lock(&serial->shared_int->shared_int_lock);
27388c2ecf20Sopenharmony_ci	serial->shared_int->ref_count++;
27398c2ecf20Sopenharmony_ci	mutex_unlock(&serial->shared_int->shared_int_lock);
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	/* setup the proc dirs and files if needed */
27428c2ecf20Sopenharmony_ci	hso_log_port(hso_dev);
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_ci	/* done, return it */
27458c2ecf20Sopenharmony_ci	return hso_dev;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_cierr_free_serial:
27488c2ecf20Sopenharmony_ci	kfree(serial);
27498c2ecf20Sopenharmony_cierr_free_dev:
27508c2ecf20Sopenharmony_ci	kfree(hso_dev);
27518c2ecf20Sopenharmony_ci	return NULL;
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci}
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_cistatic void hso_free_shared_int(struct hso_shared_int *mux)
27568c2ecf20Sopenharmony_ci{
27578c2ecf20Sopenharmony_ci	usb_free_urb(mux->shared_intr_urb);
27588c2ecf20Sopenharmony_ci	kfree(mux->shared_intr_buf);
27598c2ecf20Sopenharmony_ci	mutex_unlock(&mux->shared_int_lock);
27608c2ecf20Sopenharmony_ci	kfree(mux);
27618c2ecf20Sopenharmony_ci}
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_cistatic
27648c2ecf20Sopenharmony_cistruct hso_shared_int *hso_create_shared_int(struct usb_interface *interface)
27658c2ecf20Sopenharmony_ci{
27668c2ecf20Sopenharmony_ci	struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	if (!mux)
27698c2ecf20Sopenharmony_ci		return NULL;
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci	mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT,
27728c2ecf20Sopenharmony_ci				    USB_DIR_IN);
27738c2ecf20Sopenharmony_ci	if (!mux->intr_endp) {
27748c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Can't find INT IN endpoint\n");
27758c2ecf20Sopenharmony_ci		goto exit;
27768c2ecf20Sopenharmony_ci	}
27778c2ecf20Sopenharmony_ci
27788c2ecf20Sopenharmony_ci	mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL);
27798c2ecf20Sopenharmony_ci	if (!mux->shared_intr_urb)
27808c2ecf20Sopenharmony_ci		goto exit;
27818c2ecf20Sopenharmony_ci	mux->shared_intr_buf =
27828c2ecf20Sopenharmony_ci		kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize),
27838c2ecf20Sopenharmony_ci			GFP_KERNEL);
27848c2ecf20Sopenharmony_ci	if (!mux->shared_intr_buf)
27858c2ecf20Sopenharmony_ci		goto exit;
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci	mutex_init(&mux->shared_int_lock);
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	return mux;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ciexit:
27928c2ecf20Sopenharmony_ci	kfree(mux->shared_intr_buf);
27938c2ecf20Sopenharmony_ci	usb_free_urb(mux->shared_intr_urb);
27948c2ecf20Sopenharmony_ci	kfree(mux);
27958c2ecf20Sopenharmony_ci	return NULL;
27968c2ecf20Sopenharmony_ci}
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci/* Gets the port spec for a certain interface */
27998c2ecf20Sopenharmony_cistatic int hso_get_config_data(struct usb_interface *interface)
28008c2ecf20Sopenharmony_ci{
28018c2ecf20Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(interface);
28028c2ecf20Sopenharmony_ci	u8 *config_data = kmalloc(17, GFP_KERNEL);
28038c2ecf20Sopenharmony_ci	u32 if_num = interface->cur_altsetting->desc.bInterfaceNumber;
28048c2ecf20Sopenharmony_ci	s32 result;
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	if (!config_data)
28078c2ecf20Sopenharmony_ci		return -ENOMEM;
28088c2ecf20Sopenharmony_ci	if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
28098c2ecf20Sopenharmony_ci			    0x86, 0xC0, 0, 0, config_data, 17,
28108c2ecf20Sopenharmony_ci			    USB_CTRL_SET_TIMEOUT) != 0x11) {
28118c2ecf20Sopenharmony_ci		kfree(config_data);
28128c2ecf20Sopenharmony_ci		return -EIO;
28138c2ecf20Sopenharmony_ci	}
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_ci	/* check if we have a valid interface */
28168c2ecf20Sopenharmony_ci	if (if_num > 16) {
28178c2ecf20Sopenharmony_ci		kfree(config_data);
28188c2ecf20Sopenharmony_ci		return -EINVAL;
28198c2ecf20Sopenharmony_ci	}
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	switch (config_data[if_num]) {
28228c2ecf20Sopenharmony_ci	case 0x0:
28238c2ecf20Sopenharmony_ci		result = 0;
28248c2ecf20Sopenharmony_ci		break;
28258c2ecf20Sopenharmony_ci	case 0x1:
28268c2ecf20Sopenharmony_ci		result = HSO_PORT_DIAG;
28278c2ecf20Sopenharmony_ci		break;
28288c2ecf20Sopenharmony_ci	case 0x2:
28298c2ecf20Sopenharmony_ci		result = HSO_PORT_GPS;
28308c2ecf20Sopenharmony_ci		break;
28318c2ecf20Sopenharmony_ci	case 0x3:
28328c2ecf20Sopenharmony_ci		result = HSO_PORT_GPS_CONTROL;
28338c2ecf20Sopenharmony_ci		break;
28348c2ecf20Sopenharmony_ci	case 0x4:
28358c2ecf20Sopenharmony_ci		result = HSO_PORT_APP;
28368c2ecf20Sopenharmony_ci		break;
28378c2ecf20Sopenharmony_ci	case 0x5:
28388c2ecf20Sopenharmony_ci		result = HSO_PORT_APP2;
28398c2ecf20Sopenharmony_ci		break;
28408c2ecf20Sopenharmony_ci	case 0x6:
28418c2ecf20Sopenharmony_ci		result = HSO_PORT_CONTROL;
28428c2ecf20Sopenharmony_ci		break;
28438c2ecf20Sopenharmony_ci	case 0x7:
28448c2ecf20Sopenharmony_ci		result = HSO_PORT_NETWORK;
28458c2ecf20Sopenharmony_ci		break;
28468c2ecf20Sopenharmony_ci	case 0x8:
28478c2ecf20Sopenharmony_ci		result = HSO_PORT_MODEM;
28488c2ecf20Sopenharmony_ci		break;
28498c2ecf20Sopenharmony_ci	case 0x9:
28508c2ecf20Sopenharmony_ci		result = HSO_PORT_MSD;
28518c2ecf20Sopenharmony_ci		break;
28528c2ecf20Sopenharmony_ci	case 0xa:
28538c2ecf20Sopenharmony_ci		result = HSO_PORT_PCSC;
28548c2ecf20Sopenharmony_ci		break;
28558c2ecf20Sopenharmony_ci	case 0xb:
28568c2ecf20Sopenharmony_ci		result = HSO_PORT_VOICE;
28578c2ecf20Sopenharmony_ci		break;
28588c2ecf20Sopenharmony_ci	default:
28598c2ecf20Sopenharmony_ci		result = 0;
28608c2ecf20Sopenharmony_ci	}
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci	if (result)
28638c2ecf20Sopenharmony_ci		result |= HSO_INTF_BULK;
28648c2ecf20Sopenharmony_ci
28658c2ecf20Sopenharmony_ci	if (config_data[16] & 0x1)
28668c2ecf20Sopenharmony_ci		result |= HSO_INFO_CRC_BUG;
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	kfree(config_data);
28698c2ecf20Sopenharmony_ci	return result;
28708c2ecf20Sopenharmony_ci}
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci/* called once for each interface upon device insertion */
28738c2ecf20Sopenharmony_cistatic int hso_probe(struct usb_interface *interface,
28748c2ecf20Sopenharmony_ci		     const struct usb_device_id *id)
28758c2ecf20Sopenharmony_ci{
28768c2ecf20Sopenharmony_ci	int mux, i, if_num, port_spec;
28778c2ecf20Sopenharmony_ci	unsigned char port_mask;
28788c2ecf20Sopenharmony_ci	struct hso_device *hso_dev = NULL;
28798c2ecf20Sopenharmony_ci	struct hso_shared_int *shared_int;
28808c2ecf20Sopenharmony_ci	struct hso_device *tmp_dev = NULL;
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci	if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
28838c2ecf20Sopenharmony_ci		dev_err(&interface->dev, "Not our interface\n");
28848c2ecf20Sopenharmony_ci		return -ENODEV;
28858c2ecf20Sopenharmony_ci	}
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	if_num = interface->cur_altsetting->desc.bInterfaceNumber;
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	/* Get the interface/port specification from either driver_info or from
28908c2ecf20Sopenharmony_ci	 * the device itself */
28918c2ecf20Sopenharmony_ci	if (id->driver_info) {
28928c2ecf20Sopenharmony_ci		/* if_num is controlled by the device, driver_info is a 0 terminated
28938c2ecf20Sopenharmony_ci		 * array. Make sure, the access is in bounds! */
28948c2ecf20Sopenharmony_ci		for (i = 0; i <= if_num; ++i)
28958c2ecf20Sopenharmony_ci			if (((u32 *)(id->driver_info))[i] == 0)
28968c2ecf20Sopenharmony_ci				goto exit;
28978c2ecf20Sopenharmony_ci		port_spec = ((u32 *)(id->driver_info))[if_num];
28988c2ecf20Sopenharmony_ci	} else {
28998c2ecf20Sopenharmony_ci		port_spec = hso_get_config_data(interface);
29008c2ecf20Sopenharmony_ci		if (port_spec < 0)
29018c2ecf20Sopenharmony_ci			goto exit;
29028c2ecf20Sopenharmony_ci	}
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	/* Check if we need to switch to alt interfaces prior to port
29058c2ecf20Sopenharmony_ci	 * configuration */
29068c2ecf20Sopenharmony_ci	if (interface->num_altsetting > 1)
29078c2ecf20Sopenharmony_ci		usb_set_interface(interface_to_usbdev(interface), if_num, 1);
29088c2ecf20Sopenharmony_ci	interface->needs_remote_wakeup = 1;
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	/* Allocate new hso device(s) */
29118c2ecf20Sopenharmony_ci	switch (port_spec & HSO_INTF_MASK) {
29128c2ecf20Sopenharmony_ci	case HSO_INTF_MUX:
29138c2ecf20Sopenharmony_ci		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
29148c2ecf20Sopenharmony_ci			/* Create the network device */
29158c2ecf20Sopenharmony_ci			if (!disable_net) {
29168c2ecf20Sopenharmony_ci				hso_dev = hso_create_net_device(interface,
29178c2ecf20Sopenharmony_ci								port_spec);
29188c2ecf20Sopenharmony_ci				if (!hso_dev)
29198c2ecf20Sopenharmony_ci					goto exit;
29208c2ecf20Sopenharmony_ci				tmp_dev = hso_dev;
29218c2ecf20Sopenharmony_ci			}
29228c2ecf20Sopenharmony_ci		}
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci		if (hso_get_mux_ports(interface, &port_mask))
29258c2ecf20Sopenharmony_ci			/* TODO: de-allocate everything */
29268c2ecf20Sopenharmony_ci			goto exit;
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci		shared_int = hso_create_shared_int(interface);
29298c2ecf20Sopenharmony_ci		if (!shared_int)
29308c2ecf20Sopenharmony_ci			goto exit;
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci		for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) {
29338c2ecf20Sopenharmony_ci			if (port_mask & i) {
29348c2ecf20Sopenharmony_ci				hso_dev = hso_create_mux_serial_device(
29358c2ecf20Sopenharmony_ci						interface, i, shared_int);
29368c2ecf20Sopenharmony_ci				if (!hso_dev)
29378c2ecf20Sopenharmony_ci					goto exit;
29388c2ecf20Sopenharmony_ci			}
29398c2ecf20Sopenharmony_ci		}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci		if (tmp_dev)
29428c2ecf20Sopenharmony_ci			hso_dev = tmp_dev;
29438c2ecf20Sopenharmony_ci		break;
29448c2ecf20Sopenharmony_ci
29458c2ecf20Sopenharmony_ci	case HSO_INTF_BULK:
29468c2ecf20Sopenharmony_ci		/* It's a regular bulk interface */
29478c2ecf20Sopenharmony_ci		if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
29488c2ecf20Sopenharmony_ci			if (!disable_net)
29498c2ecf20Sopenharmony_ci				hso_dev =
29508c2ecf20Sopenharmony_ci				    hso_create_net_device(interface, port_spec);
29518c2ecf20Sopenharmony_ci		} else {
29528c2ecf20Sopenharmony_ci			hso_dev =
29538c2ecf20Sopenharmony_ci			    hso_create_bulk_serial_device(interface, port_spec);
29548c2ecf20Sopenharmony_ci		}
29558c2ecf20Sopenharmony_ci		if (!hso_dev)
29568c2ecf20Sopenharmony_ci			goto exit;
29578c2ecf20Sopenharmony_ci		break;
29588c2ecf20Sopenharmony_ci	default:
29598c2ecf20Sopenharmony_ci		goto exit;
29608c2ecf20Sopenharmony_ci	}
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_ci	/* save our data pointer in this device */
29638c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, hso_dev);
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	/* done */
29668c2ecf20Sopenharmony_ci	return 0;
29678c2ecf20Sopenharmony_ciexit:
29688c2ecf20Sopenharmony_ci	hso_free_interface(interface);
29698c2ecf20Sopenharmony_ci	return -ENODEV;
29708c2ecf20Sopenharmony_ci}
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci/* device removed, cleaning up */
29738c2ecf20Sopenharmony_cistatic void hso_disconnect(struct usb_interface *interface)
29748c2ecf20Sopenharmony_ci{
29758c2ecf20Sopenharmony_ci	hso_free_interface(interface);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	/* remove reference of our private data */
29788c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, NULL);
29798c2ecf20Sopenharmony_ci}
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_cistatic void async_get_intf(struct work_struct *data)
29828c2ecf20Sopenharmony_ci{
29838c2ecf20Sopenharmony_ci	struct hso_device *hso_dev =
29848c2ecf20Sopenharmony_ci	    container_of(data, struct hso_device, async_get_intf);
29858c2ecf20Sopenharmony_ci	usb_autopm_get_interface(hso_dev->interface);
29868c2ecf20Sopenharmony_ci}
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_cistatic void async_put_intf(struct work_struct *data)
29898c2ecf20Sopenharmony_ci{
29908c2ecf20Sopenharmony_ci	struct hso_device *hso_dev =
29918c2ecf20Sopenharmony_ci	    container_of(data, struct hso_device, async_put_intf);
29928c2ecf20Sopenharmony_ci	usb_autopm_put_interface(hso_dev->interface);
29938c2ecf20Sopenharmony_ci}
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_cistatic int hso_get_activity(struct hso_device *hso_dev)
29968c2ecf20Sopenharmony_ci{
29978c2ecf20Sopenharmony_ci	if (hso_dev->usb->state == USB_STATE_SUSPENDED) {
29988c2ecf20Sopenharmony_ci		if (!hso_dev->is_active) {
29998c2ecf20Sopenharmony_ci			hso_dev->is_active = 1;
30008c2ecf20Sopenharmony_ci			schedule_work(&hso_dev->async_get_intf);
30018c2ecf20Sopenharmony_ci		}
30028c2ecf20Sopenharmony_ci	}
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	if (hso_dev->usb->state != USB_STATE_CONFIGURED)
30058c2ecf20Sopenharmony_ci		return -EAGAIN;
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_ci	usb_mark_last_busy(hso_dev->usb);
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	return 0;
30108c2ecf20Sopenharmony_ci}
30118c2ecf20Sopenharmony_ci
30128c2ecf20Sopenharmony_cistatic int hso_put_activity(struct hso_device *hso_dev)
30138c2ecf20Sopenharmony_ci{
30148c2ecf20Sopenharmony_ci	if (hso_dev->usb->state != USB_STATE_SUSPENDED) {
30158c2ecf20Sopenharmony_ci		if (hso_dev->is_active) {
30168c2ecf20Sopenharmony_ci			hso_dev->is_active = 0;
30178c2ecf20Sopenharmony_ci			schedule_work(&hso_dev->async_put_intf);
30188c2ecf20Sopenharmony_ci			return -EAGAIN;
30198c2ecf20Sopenharmony_ci		}
30208c2ecf20Sopenharmony_ci	}
30218c2ecf20Sopenharmony_ci	hso_dev->is_active = 0;
30228c2ecf20Sopenharmony_ci	return 0;
30238c2ecf20Sopenharmony_ci}
30248c2ecf20Sopenharmony_ci
30258c2ecf20Sopenharmony_ci/* called by kernel when we need to suspend device */
30268c2ecf20Sopenharmony_cistatic int hso_suspend(struct usb_interface *iface, pm_message_t message)
30278c2ecf20Sopenharmony_ci{
30288c2ecf20Sopenharmony_ci	int i, result;
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci	/* Stop all serial ports */
30318c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
30328c2ecf20Sopenharmony_ci		if (serial_table[i] && (serial_table[i]->interface == iface)) {
30338c2ecf20Sopenharmony_ci			result = hso_stop_serial_device(serial_table[i]);
30348c2ecf20Sopenharmony_ci			if (result)
30358c2ecf20Sopenharmony_ci				goto out;
30368c2ecf20Sopenharmony_ci		}
30378c2ecf20Sopenharmony_ci	}
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	/* Stop all network ports */
30408c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
30418c2ecf20Sopenharmony_ci		if (network_table[i] &&
30428c2ecf20Sopenharmony_ci		    (network_table[i]->interface == iface)) {
30438c2ecf20Sopenharmony_ci			result = hso_stop_net_device(network_table[i]);
30448c2ecf20Sopenharmony_ci			if (result)
30458c2ecf20Sopenharmony_ci				goto out;
30468c2ecf20Sopenharmony_ci		}
30478c2ecf20Sopenharmony_ci	}
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ciout:
30508c2ecf20Sopenharmony_ci	return 0;
30518c2ecf20Sopenharmony_ci}
30528c2ecf20Sopenharmony_ci
30538c2ecf20Sopenharmony_ci/* called by kernel when we need to resume device */
30548c2ecf20Sopenharmony_cistatic int hso_resume(struct usb_interface *iface)
30558c2ecf20Sopenharmony_ci{
30568c2ecf20Sopenharmony_ci	int i, result = 0;
30578c2ecf20Sopenharmony_ci	struct hso_net *hso_net;
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	/* Start all serial ports */
30608c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
30618c2ecf20Sopenharmony_ci		if (serial_table[i] && (serial_table[i]->interface == iface)) {
30628c2ecf20Sopenharmony_ci			if (dev2ser(serial_table[i])->port.count) {
30638c2ecf20Sopenharmony_ci				result =
30648c2ecf20Sopenharmony_ci				    hso_start_serial_device(serial_table[i], GFP_NOIO);
30658c2ecf20Sopenharmony_ci				hso_kick_transmit(dev2ser(serial_table[i]));
30668c2ecf20Sopenharmony_ci				if (result)
30678c2ecf20Sopenharmony_ci					goto out;
30688c2ecf20Sopenharmony_ci			}
30698c2ecf20Sopenharmony_ci		}
30708c2ecf20Sopenharmony_ci	}
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_ci	/* Start all network ports */
30738c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
30748c2ecf20Sopenharmony_ci		if (network_table[i] &&
30758c2ecf20Sopenharmony_ci		    (network_table[i]->interface == iface)) {
30768c2ecf20Sopenharmony_ci			hso_net = dev2net(network_table[i]);
30778c2ecf20Sopenharmony_ci			if (hso_net->flags & IFF_UP) {
30788c2ecf20Sopenharmony_ci				/* First transmit any lingering data,
30798c2ecf20Sopenharmony_ci				   then restart the device. */
30808c2ecf20Sopenharmony_ci				if (hso_net->skb_tx_buf) {
30818c2ecf20Sopenharmony_ci					dev_dbg(&iface->dev,
30828c2ecf20Sopenharmony_ci						"Transmitting"
30838c2ecf20Sopenharmony_ci						" lingering data\n");
30848c2ecf20Sopenharmony_ci					hso_net_start_xmit(hso_net->skb_tx_buf,
30858c2ecf20Sopenharmony_ci							   hso_net->net);
30868c2ecf20Sopenharmony_ci					hso_net->skb_tx_buf = NULL;
30878c2ecf20Sopenharmony_ci				}
30888c2ecf20Sopenharmony_ci				result = hso_start_net_device(network_table[i]);
30898c2ecf20Sopenharmony_ci				if (result)
30908c2ecf20Sopenharmony_ci					goto out;
30918c2ecf20Sopenharmony_ci			}
30928c2ecf20Sopenharmony_ci		}
30938c2ecf20Sopenharmony_ci	}
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_ciout:
30968c2ecf20Sopenharmony_ci	return result;
30978c2ecf20Sopenharmony_ci}
30988c2ecf20Sopenharmony_ci
30998c2ecf20Sopenharmony_cistatic void hso_serial_ref_free(struct kref *ref)
31008c2ecf20Sopenharmony_ci{
31018c2ecf20Sopenharmony_ci	struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	hso_free_serial_device(hso_dev);
31048c2ecf20Sopenharmony_ci}
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_cistatic void hso_free_interface(struct usb_interface *interface)
31078c2ecf20Sopenharmony_ci{
31088c2ecf20Sopenharmony_ci	struct hso_serial *serial;
31098c2ecf20Sopenharmony_ci	int i;
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
31128c2ecf20Sopenharmony_ci		if (serial_table[i] &&
31138c2ecf20Sopenharmony_ci		    (serial_table[i]->interface == interface)) {
31148c2ecf20Sopenharmony_ci			serial = dev2ser(serial_table[i]);
31158c2ecf20Sopenharmony_ci			tty_port_tty_hangup(&serial->port, false);
31168c2ecf20Sopenharmony_ci			mutex_lock(&serial->parent->mutex);
31178c2ecf20Sopenharmony_ci			serial->parent->usb_gone = 1;
31188c2ecf20Sopenharmony_ci			mutex_unlock(&serial->parent->mutex);
31198c2ecf20Sopenharmony_ci			cancel_work_sync(&serial_table[i]->async_put_intf);
31208c2ecf20Sopenharmony_ci			cancel_work_sync(&serial_table[i]->async_get_intf);
31218c2ecf20Sopenharmony_ci			hso_serial_tty_unregister(serial);
31228c2ecf20Sopenharmony_ci			kref_put(&serial->parent->ref, hso_serial_ref_free);
31238c2ecf20Sopenharmony_ci		}
31248c2ecf20Sopenharmony_ci	}
31258c2ecf20Sopenharmony_ci
31268c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
31278c2ecf20Sopenharmony_ci		if (network_table[i] &&
31288c2ecf20Sopenharmony_ci		    (network_table[i]->interface == interface)) {
31298c2ecf20Sopenharmony_ci			struct rfkill *rfk = dev2net(network_table[i])->rfkill;
31308c2ecf20Sopenharmony_ci			/* hso_stop_net_device doesn't stop the net queue since
31318c2ecf20Sopenharmony_ci			 * traffic needs to start it again when suspended */
31328c2ecf20Sopenharmony_ci			netif_stop_queue(dev2net(network_table[i])->net);
31338c2ecf20Sopenharmony_ci			hso_stop_net_device(network_table[i]);
31348c2ecf20Sopenharmony_ci			cancel_work_sync(&network_table[i]->async_put_intf);
31358c2ecf20Sopenharmony_ci			cancel_work_sync(&network_table[i]->async_get_intf);
31368c2ecf20Sopenharmony_ci			if (rfk) {
31378c2ecf20Sopenharmony_ci				rfkill_unregister(rfk);
31388c2ecf20Sopenharmony_ci				rfkill_destroy(rfk);
31398c2ecf20Sopenharmony_ci			}
31408c2ecf20Sopenharmony_ci			hso_free_net_device(network_table[i]);
31418c2ecf20Sopenharmony_ci		}
31428c2ecf20Sopenharmony_ci	}
31438c2ecf20Sopenharmony_ci}
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci/* Helper functions */
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci/* Get the endpoint ! */
31488c2ecf20Sopenharmony_cistatic struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
31498c2ecf20Sopenharmony_ci						  int type, int dir)
31508c2ecf20Sopenharmony_ci{
31518c2ecf20Sopenharmony_ci	int i;
31528c2ecf20Sopenharmony_ci	struct usb_host_interface *iface = intf->cur_altsetting;
31538c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endp;
31548c2ecf20Sopenharmony_ci
31558c2ecf20Sopenharmony_ci	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
31568c2ecf20Sopenharmony_ci		endp = &iface->endpoint[i].desc;
31578c2ecf20Sopenharmony_ci		if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
31588c2ecf20Sopenharmony_ci		    (usb_endpoint_type(endp) == type))
31598c2ecf20Sopenharmony_ci			return endp;
31608c2ecf20Sopenharmony_ci	}
31618c2ecf20Sopenharmony_ci
31628c2ecf20Sopenharmony_ci	return NULL;
31638c2ecf20Sopenharmony_ci}
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci/* Get the byte that describes which ports are enabled */
31668c2ecf20Sopenharmony_cistatic int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports)
31678c2ecf20Sopenharmony_ci{
31688c2ecf20Sopenharmony_ci	int i;
31698c2ecf20Sopenharmony_ci	struct usb_host_interface *iface = intf->cur_altsetting;
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_ci	if (iface->extralen == 3) {
31728c2ecf20Sopenharmony_ci		*ports = iface->extra[2];
31738c2ecf20Sopenharmony_ci		return 0;
31748c2ecf20Sopenharmony_ci	}
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci	for (i = 0; i < iface->desc.bNumEndpoints; i++) {
31778c2ecf20Sopenharmony_ci		if (iface->endpoint[i].extralen == 3) {
31788c2ecf20Sopenharmony_ci			*ports = iface->endpoint[i].extra[2];
31798c2ecf20Sopenharmony_ci			return 0;
31808c2ecf20Sopenharmony_ci		}
31818c2ecf20Sopenharmony_ci	}
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci	return -1;
31848c2ecf20Sopenharmony_ci}
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci/* interrupt urb needs to be submitted, used for serial read of muxed port */
31878c2ecf20Sopenharmony_cistatic int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int,
31888c2ecf20Sopenharmony_ci				   struct usb_device *usb, gfp_t gfp)
31898c2ecf20Sopenharmony_ci{
31908c2ecf20Sopenharmony_ci	int result;
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci	usb_fill_int_urb(shared_int->shared_intr_urb, usb,
31938c2ecf20Sopenharmony_ci			 usb_rcvintpipe(usb,
31948c2ecf20Sopenharmony_ci				shared_int->intr_endp->bEndpointAddress & 0x7F),
31958c2ecf20Sopenharmony_ci			 shared_int->shared_intr_buf,
31968c2ecf20Sopenharmony_ci			 1,
31978c2ecf20Sopenharmony_ci			 intr_callback, shared_int,
31988c2ecf20Sopenharmony_ci			 shared_int->intr_endp->bInterval);
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ci	result = usb_submit_urb(shared_int->shared_intr_urb, gfp);
32018c2ecf20Sopenharmony_ci	if (result)
32028c2ecf20Sopenharmony_ci		dev_warn(&usb->dev, "%s failed mux_intr_urb %d\n", __func__,
32038c2ecf20Sopenharmony_ci			result);
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_ci	return result;
32068c2ecf20Sopenharmony_ci}
32078c2ecf20Sopenharmony_ci
32088c2ecf20Sopenharmony_ci/* operations setup of the serial interface */
32098c2ecf20Sopenharmony_cistatic const struct tty_operations hso_serial_ops = {
32108c2ecf20Sopenharmony_ci	.open = hso_serial_open,
32118c2ecf20Sopenharmony_ci	.close = hso_serial_close,
32128c2ecf20Sopenharmony_ci	.write = hso_serial_write,
32138c2ecf20Sopenharmony_ci	.write_room = hso_serial_write_room,
32148c2ecf20Sopenharmony_ci	.cleanup = hso_serial_cleanup,
32158c2ecf20Sopenharmony_ci	.ioctl = hso_serial_ioctl,
32168c2ecf20Sopenharmony_ci	.set_termios = hso_serial_set_termios,
32178c2ecf20Sopenharmony_ci	.chars_in_buffer = hso_serial_chars_in_buffer,
32188c2ecf20Sopenharmony_ci	.tiocmget = hso_serial_tiocmget,
32198c2ecf20Sopenharmony_ci	.tiocmset = hso_serial_tiocmset,
32208c2ecf20Sopenharmony_ci	.get_icount = hso_get_count,
32218c2ecf20Sopenharmony_ci	.unthrottle = hso_unthrottle
32228c2ecf20Sopenharmony_ci};
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_cistatic struct usb_driver hso_driver = {
32258c2ecf20Sopenharmony_ci	.name = driver_name,
32268c2ecf20Sopenharmony_ci	.probe = hso_probe,
32278c2ecf20Sopenharmony_ci	.disconnect = hso_disconnect,
32288c2ecf20Sopenharmony_ci	.id_table = hso_ids,
32298c2ecf20Sopenharmony_ci	.suspend = hso_suspend,
32308c2ecf20Sopenharmony_ci	.resume = hso_resume,
32318c2ecf20Sopenharmony_ci	.reset_resume = hso_resume,
32328c2ecf20Sopenharmony_ci	.supports_autosuspend = 1,
32338c2ecf20Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
32348c2ecf20Sopenharmony_ci};
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_cistatic int __init hso_init(void)
32378c2ecf20Sopenharmony_ci{
32388c2ecf20Sopenharmony_ci	int i;
32398c2ecf20Sopenharmony_ci	int result;
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_ci	/* put it in the log */
32428c2ecf20Sopenharmony_ci	pr_info("%s\n", version);
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_ci	/* Initialise the serial table semaphore and table */
32458c2ecf20Sopenharmony_ci	spin_lock_init(&serial_table_lock);
32468c2ecf20Sopenharmony_ci	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
32478c2ecf20Sopenharmony_ci		serial_table[i] = NULL;
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci	/* allocate our driver using the proper amount of supported minors */
32508c2ecf20Sopenharmony_ci	tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS);
32518c2ecf20Sopenharmony_ci	if (!tty_drv)
32528c2ecf20Sopenharmony_ci		return -ENOMEM;
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci	/* fill in all needed values */
32558c2ecf20Sopenharmony_ci	tty_drv->driver_name = driver_name;
32568c2ecf20Sopenharmony_ci	tty_drv->name = tty_filename;
32578c2ecf20Sopenharmony_ci
32588c2ecf20Sopenharmony_ci	/* if major number is provided as parameter, use that one */
32598c2ecf20Sopenharmony_ci	if (tty_major)
32608c2ecf20Sopenharmony_ci		tty_drv->major = tty_major;
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci	tty_drv->minor_start = 0;
32638c2ecf20Sopenharmony_ci	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
32648c2ecf20Sopenharmony_ci	tty_drv->subtype = SERIAL_TYPE_NORMAL;
32658c2ecf20Sopenharmony_ci	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
32668c2ecf20Sopenharmony_ci	tty_drv->init_termios = tty_std_termios;
32678c2ecf20Sopenharmony_ci	hso_init_termios(&tty_drv->init_termios);
32688c2ecf20Sopenharmony_ci	tty_set_operations(tty_drv, &hso_serial_ops);
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci	/* register the tty driver */
32718c2ecf20Sopenharmony_ci	result = tty_register_driver(tty_drv);
32728c2ecf20Sopenharmony_ci	if (result) {
32738c2ecf20Sopenharmony_ci		pr_err("%s - tty_register_driver failed(%d)\n",
32748c2ecf20Sopenharmony_ci		       __func__, result);
32758c2ecf20Sopenharmony_ci		goto err_free_tty;
32768c2ecf20Sopenharmony_ci	}
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci	/* register this module as an usb driver */
32798c2ecf20Sopenharmony_ci	result = usb_register(&hso_driver);
32808c2ecf20Sopenharmony_ci	if (result) {
32818c2ecf20Sopenharmony_ci		pr_err("Could not register hso driver - error: %d\n", result);
32828c2ecf20Sopenharmony_ci		goto err_unreg_tty;
32838c2ecf20Sopenharmony_ci	}
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci	/* done */
32868c2ecf20Sopenharmony_ci	return 0;
32878c2ecf20Sopenharmony_cierr_unreg_tty:
32888c2ecf20Sopenharmony_ci	tty_unregister_driver(tty_drv);
32898c2ecf20Sopenharmony_cierr_free_tty:
32908c2ecf20Sopenharmony_ci	put_tty_driver(tty_drv);
32918c2ecf20Sopenharmony_ci	return result;
32928c2ecf20Sopenharmony_ci}
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_cistatic void __exit hso_exit(void)
32958c2ecf20Sopenharmony_ci{
32968c2ecf20Sopenharmony_ci	pr_info("unloaded\n");
32978c2ecf20Sopenharmony_ci
32988c2ecf20Sopenharmony_ci	tty_unregister_driver(tty_drv);
32998c2ecf20Sopenharmony_ci	/* deregister the usb driver */
33008c2ecf20Sopenharmony_ci	usb_deregister(&hso_driver);
33018c2ecf20Sopenharmony_ci	put_tty_driver(tty_drv);
33028c2ecf20Sopenharmony_ci}
33038c2ecf20Sopenharmony_ci
33048c2ecf20Sopenharmony_ci/* Module definitions */
33058c2ecf20Sopenharmony_cimodule_init(hso_init);
33068c2ecf20Sopenharmony_cimodule_exit(hso_exit);
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ciMODULE_AUTHOR(MOD_AUTHOR);
33098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(MOD_DESCRIPTION);
33108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci/* change the debug level (eg: insmod hso.ko debug=0x04) */
33138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level mask [0x01 | 0x02 | 0x04 | 0x08 | 0x10]");
33148c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
33158c2ecf20Sopenharmony_ci
33168c2ecf20Sopenharmony_ci/* set the major tty number (eg: insmod hso.ko tty_major=245) */
33178c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tty_major, "Set the major tty number");
33188c2ecf20Sopenharmony_cimodule_param(tty_major, int, 0644);
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci/* disable network interface (eg: insmod hso.ko disable_net=1) */
33218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_net, "Disable the network interface");
33228c2ecf20Sopenharmony_cimodule_param(disable_net, int, 0644);
3323