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