?? hso.c
字號:
/****************************************************************************** * * Driver for Option High Speed Mobile Devices. * * Copyright (C) 2008 Option International * Filip Aben <f.aben@option.com> * Denis Joseph Barrow <d.barow@option.com> * Jan Dumon <j.dumon@option.com> * Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd) * <ajb@spheresystems.co.uk> * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> * Copyright (C) 2008 Novell, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA * * *****************************************************************************//****************************************************************************** * * Description of the device: * * Interface 0: Contains the IP network interface on the bulk end points. * The multiplexed serial ports are using the interrupt and * control endpoints. * Interrupt contains a bitmap telling which multiplexed * serialport needs servicing. * * Interface 1: Diagnostics port, uses bulk only, do not submit urbs until the * port is opened, as this have a huge impact on the network port * throughput. * * Interface 2: Standard modem interface - circuit switched interface, this * can be used to make a standard ppp connection however it * should not be used in conjunction with the IP network interface * enabled for USB performance reasons i.e. if using this set * ideally disable_net=1. * *****************************************************************************/#include <linux/version.h>#include <linux/autoconf.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/module.h>#include <linux/ethtool.h>#include <linux/usb.h>#include <linux/timer.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/kmod.h>#define HAVE_RFKILL (defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE))#if (HAVE_RFKILL)#include <linux/rfkill.h>#endif#include <linux/ip.h>#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))#include <linux/uaccess.h>#else#include <asm/uaccess.h>#endif#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))#include <linux/usb/cdc.h>#else#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 11))#include <linux/usb_cdc.h>#else#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x0#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x1#endif#endif#include <net/arp.h>#include <asm/byteorder.h>#include <linux/serial_core.h>#include <linux/serial.h>#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15))#include <linux/mutex.h>#define MUTEX mutex#else#include <asm/semaphore.h>#define MUTEX semaphore#define mutex_init(sem) sema_init(sem, 1)#define mutex_lock(sem) down(sem)#define mutex_unlock(sem) up(sem)#endif /* ( LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 15) ) */#define NETDEV_HAVE_DEV (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20))#if NETDEV_HAVE_DEV#define netdev_err(net, format, arg...) dev_err(&net->dev, format, ## arg)#define netdev_warn(net, format, arg...) dev_warn(&net->dev, format, ## arg)#else#define netdev_err(net, format, arg...) printk(KERN_ERR "%s" format ,\ (char *)(&net->name), ## arg)#define netdev_warn(net, format, arg...) printk(KERN_WARNING "%s" format ,\ (char *)(&net->name), ## arg)#endif#ifdef CONFIG_HSO_AUTOPM#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))#warning "CONFIG_HSO_AUTOPM set for kernel version < 2.6.19," \ "it's not supported by the kernel I'm undeffing it."#undef CONFIG_HSO_AUTOPM#endif#endif#define HAVE_RESET_RESUME (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))#if (!HAVE_RFKILL)#define SIOCSETRADIO (SIOCDEVPRIVATE+4)#endif#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22))static inline void skb_reset_mac_header(struct sk_buff *skb){ skb->mac.raw = skb->data;}#endif#ifdef CONFIG_HSO_AUTOPM#define sCONFIG_HSO_AUTOPM "CONFIG_HSO_AUTOPM "#else#define sCONFIG_HSO_AUTOPM ""#endif#ifdef CONFIG_HSO_DEBUG#define sCONFIG_HSO_DEBUG "CONFIG_HSO_DEBUG "#else#define sCONFIG_HSO_DEBUG ""#endif#define DRIVER_VERSION "1.12-Option"#define MOD_AUTHOR "Option Wireless"#define MOD_DESCRIPTION "USB High Speed Option driver"#define MOD_LICENSE "GPL"#define HSO_MAX_NET_DEVICES 10#define HSO_MAX_MTU 2048#define DEFAULT_MTU 1500#define DEFAULT_MRU 1500#define CTRL_URB_RX_SIZE 1024#define CTRL_URB_TX_SIZE 64#define BULK_URB_RX_SIZE 4096#define BULK_URB_TX_SIZE 8192#define MUX_BULK_RX_BUF_SIZE HSO_MAX_MTU#define MUX_BULK_TX_BUF_SIZE HSO_MAX_MTU#define MUX_BULK_RX_BUF_COUNT 4#define USB_TYPE_OPTION_VENDOR 0x20/* These definitions are used with the struct hso_net flags element *//* - use *_bit operations on it. (bit indices not values.) */#define HSO_NET_RUNNING 0#define HSO_NET_TX_TIMEOUT (HZ*10)#define HSO_SERIAL_MAGIC 0x48534f31/* Number of ttys to handle */#define HSO_SERIAL_TTY_MINORS 256#define MAX_RX_URBS 2/*****************************************************************************//* kernel dependent declarations *//*****************************************************************************/#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20))#define TERMIOS termios#define GFP_T int#define CALLBACK_ARGS struct urb *urb, struct pt_regs *regs#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) */#define TERMIOS ktermios#define GFP_T gfp_t#define CALLBACK_ARGS struct urb *urb#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) */static inline struct hso_serial *get_serial_by_tty(struct tty_struct *tty){ if (tty) return tty->driver_data; return NULL;}/*****************************************************************************//* Debugging functions *//*****************************************************************************/#define D__(lvl_, fmt, arg...) \ do { \ printk(lvl_ "[%d:%s]: " fmt "\n", \ __LINE__, __func__, ## arg); \ } while (0)#define D_(lvl, args...) \ do { \ if (lvl & debug) \ D__(KERN_INFO, args); \ } while (0)#define D1(args...) D_(0x01, ##args)#define D2(args...) D_(0x02, ##args)#define D3(args...) D_(0x04, ##args)#define D4(args...) D_(0x08, ##args)#define D5(args...) D_(0x10, ##args)/*****************************************************************************//* Enumerators *//*****************************************************************************/enum pkt_parse_state { WAIT_IP, WAIT_DATA, WAIT_SYNC};/*****************************************************************************//* Structs *//*****************************************************************************/struct hso_shared_int { struct usb_endpoint_descriptor *intr_endp; void *shared_intr_buf; struct urb *shared_intr_urb; struct usb_device *usb; int use_count; int ref_count; struct MUTEX shared_int_lock;};#define NETDEVICE_HAS_STATS (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))struct hso_net { struct hso_device *parent;#if (!NETDEVICE_HAS_STATS) struct net_device_stats stats;#endif struct net_device *net;#if HAVE_RFKILL struct rfkill *rfkill;#endif struct usb_endpoint_descriptor *in_endp; struct usb_endpoint_descriptor *out_endp; struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT]; struct urb *mux_bulk_tx_urb; void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT]; void *mux_bulk_tx_buf; struct sk_buff *skb_rx_buf; struct sk_buff *skb_tx_buf; enum pkt_parse_state rx_parse_state; spinlock_t net_lock; unsigned short rx_buf_size; unsigned short rx_buf_missing; struct iphdr rx_ip_hdr; unsigned long flags;};#if NETDEVICE_HAS_STATS#define STATS(net) ((net)->stats)#else#define STATS(net) (((struct hso_net *)netdev_priv(net))->stats)#endif#define HAVE_TTY_INSERT_FLIP_STRING \(LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16))#define HAVE_LDISC_OPS (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16))enum rx_ctrl_state{ RX_IDLE, RX_SENT, RX_PENDING};#define BM_REQUEST_TYPE (0xa1)#define B_NOTIFICATION (0x20)#define W_VALUE (0x0)#define W_INDEX (0x2)#define W_LENGTH (0x2)#define B_OVERRUN (0x1<<6)#define B_PARITY (0x1<<5)#define B_FRAMING (0x1<<4)#define B_RING_SIGNAL (0x1<<3)#define B_BREAK (0x1<<2)#define B_TX_CARRIER (0x1<<1)#define B_RX_CARRIER (0x1<<0)struct hso_serial_state_notification { u8 bmRequestType; u8 bNotification; u16 wValue; u16 wIndex; u16 wLength; u16 UART_state_bitmap;} __attribute__((packed));struct hso_tiocmget { struct MUTEX mutex; wait_queue_head_t waitq; int intr_completed; struct usb_endpoint_descriptor *endp; struct urb *urb; struct hso_serial_state_notification serial_state_notification; u16 prev_UART_state_bitmap; struct uart_icount icount;};struct hso_serial { struct hso_device *parent; int magic; u8 minor; struct hso_shared_int *shared_int; /* rx/tx urb could be either a bulk urb or a control urb depending on which serial port it is used on. */ struct urb *rx_urb[MAX_RX_URBS]; u8 num_rx_urbs; u8 *rx_data[MAX_RX_URBS]; u16 rx_data_length; /* should contain allocated length */ struct urb *tx_urb; u8 *tx_data; u8 *tx_buffer; u16 tx_data_length; /* should contain allocated length */ u16 tx_data_count; u16 tx_buffer_count; struct usb_ctrlrequest ctrl_req_tx; struct usb_ctrlrequest ctrl_req_rx; struct usb_endpoint_descriptor *in_endp; struct usb_endpoint_descriptor *out_endp; enum rx_ctrl_state rx_state; u8 rts_state; u8 dtr_state; unsigned tx_urb_used:1; /* from usb_serial_port */ struct tty_struct *tty; int open_count; spinlock_t serial_lock; int (*write_data) (struct hso_serial *serial); /* Hacks required to get flow control * working on the serial receive buffers * so as not to drop characters on the floor. */ int curr_rx_urb_idx; u16 curr_rx_urb_offset; u8 rx_urb_filled[MAX_RX_URBS]; struct tasklet_struct unthrottle_tasklet; struct hso_tiocmget *tiocmget;};struct hso_mutex_t { struct MUTEX mutex; u8 allocated;};struct hso_device { union { struct hso_serial *dev_serial; struct hso_net *dev_net; } port_data; u32 port_spec; u8 usb_gone;#ifdef CONFIG_HSO_AUTOPM u8 is_active; struct work_struct async_get_intf; struct work_struct async_put_intf;#endif struct usb_device *usb; struct usb_interface *interface; struct device *dev; struct kref ref; struct hso_mutex_t *mutex;};/* Type of interface */#define HSO_INTF_MASK 0xFF00#define HSO_INTF_MUX 0x0100#define HSO_INTF_BULK 0x0200/* Type of port */#define HSO_PORT_MASK 0xFF#define HSO_PORT_NO_PORT 0x0#define HSO_PORT_CONTROL 0x1#define HSO_PORT_APP 0x2#define HSO_PORT_GPS 0x3#define HSO_PORT_PCSC 0x4#define HSO_PORT_APP2 0x5#define HSO_PORT_GPS_CONTROL 0x6#define HSO_PORT_MSD 0x7#define HSO_PORT_VOICE 0x8#define HSO_PORT_DIAG2 0x9#define HSO_PORT_DIAG 0x10#define HSO_PORT_MODEM 0x11#define HSO_PORT_NETWORK 0x12/* Additional device info */#define HSO_INFO_MASK 0xFF000000#define HSO_INFO_CRC_BUG 0x01000000/*****************************************************************************//* Prototypes *//*****************************************************************************//* Serial driver functions */static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);static void ctrl_callback(CALLBACK_ARGS);static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial);static void hso_kick_transmit(struct hso_serial *serial);/* Helper functions */static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, struct usb_device *usb, GFP_T gfp);static void log_usb_status(int status, const char *function);static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, int type, int dir);static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports);static void hso_free_interface(struct usb_interface *intf);static int hso_start_serial_device(struct hso_device *hso_dev, GFP_T flags);static int hso_stop_serial_device(struct hso_device *hso_dev);static int hso_start_net_device(struct hso_device *hso_dev);static void hso_free_shared_int(struct hso_shared_int *shared_int);static int hso_stop_net_device(struct hso_device *hso_dev);static void hso_serial_ref_free(struct kref *ref);static void hso_std_serial_read_bulk_callback(CALLBACK_ARGS);static int hso_mux_serial_read(struct hso_serial *serial);#ifdef CONFIG_HSO_AUTOPMstatic void async_get_intf(struct work_struct *data);static void async_put_intf(struct work_struct *data);static int hso_put_activity(struct hso_device *hso_dev);static int hso_get_activity(struct hso_device *hso_dev);#endifstatic void tiocmget_intr_callback(CALLBACK_ARGS);/*****************************************************************************//* Helping functions *//*****************************************************************************/static inline struct hso_net *dev2net(struct hso_device *hso_dev){ return hso_dev->port_data.dev_net;}static inline struct hso_serial *dev2ser(struct hso_device *hso_dev){ return hso_dev->port_data.dev_serial;}#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 23))#define safe_usb_unlink_urb usb_unlink_urb#define safe_usb_kill_urb usb_kill_urb#elsestatic void safe_usb_unlink_urb(struct urb *urb){ if (!urb)
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -