* [RFC] Patch to option HSO driver to the kernel
@ 2008-04-14 21:32 Greg KH
[not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
` (2 more replies)
0 siblings, 3 replies; 54+ messages in thread
From: Greg KH @ 2008-04-14 21:32 UTC (permalink / raw)
To: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA
Cc: Alan Cox, Filip Aben, Paulius Zaleckas,
ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io
Hi all,
Here's a patch that I have cleaned up for context only from Option that
is a USB serial / network device all in one.
I'd like to see this go into 2.6.26, so any review comments by anyone
who wishes to review any portion of this would be greatly apprecited.
Filip, can you send me some better information for the Kconfig text
talking about what devices this driver is for, and any other information
you wish to show there?
thanks,
greg k-h
-----------
Subject: USB: add option hso driver
This driver is for a number of different Option devices.
TODO:
- review tty layer interface
- review USB interfaces
- remove proc files and move to debugfs
- review network interfaces
- add better changelog information
Cc: Andrew Bird <ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io@public.gmane.org>
Cc: Alan Cox <alan-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org>
Cc: Filip Aben <f.aben-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
Cc: Paulius Zaleckas <paulius.zaleckas-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
---
drivers/net/usb/Kconfig | 10
drivers/net/usb/Makefile | 1
drivers/net/usb/hso.c | 3349 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 3360 insertions(+)
create mode 100644 drivers/net/usb/hso.c
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -154,6 +154,16 @@ config USB_NET_AX8817X
This driver creates an interface named "ethX", where X depends on
what other networking devices you have in use.
+config USB_HSO
+ tristate "Option USB High Speed Mobile Devices"
+ depends on USB
+ default n
+ help
+ Choose this option if you have an Option High Speed Mobile
+ device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hso.
config USB_NET_CDCETHER
tristate "CDC Ethernet support (smart devices such as cable modems)"
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_USB_CATC) += catc.o
obj-$(CONFIG_USB_KAWETH) += kaweth.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
+obj-$(CONFIG_USB_HSO) += hso.o
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
--- /dev/null
+++ b/drivers/net/usb/hso.c
@@ -0,0 +1,3349 @@
+/******************************************************************************
+ *
+ * Driver for Option High Speed Mobile Devices.
+ *
+ * Copyright (C) 2008 Option International
+ * Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd)
+ * <ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io@public.gmane.org>
+ *
+ * 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, should
+ * not be used.
+ *
+ *****************************************************************************/
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.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>
+#include <linux/ip.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <net/arp.h>
+#include <asm/byteorder.h>
+
+
+#define DRIVER_VERSION "1.2"
+#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_INTR_BUF_SIZE 16
+#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_BUSY 1
+#define HSO_TERMINATE 2
+
+#define HSO_NET_TX_TIMEOUT (HZ*10)
+
+#define SEND_ENCAPSULATED_COMMAND 0x00
+#define GET_ENCAPSULATED_RESPONSE 0x01
+
+/* Serial port defines and structs. */
+#define HSO_THRESHOLD_THROTTLE (7*1024)
+#define HSO_THRESHOLD_UNTHROTTLE (2*1024)
+
+/* These definitions used in the Ethernet Packet Filtering requests */
+/* See CDC Spec Table 62 */
+#define MODE_FLAG_PROMISCUOUS (1<<0)
+#define MODE_FLAG_ALL_MULTICAST (1<<1)
+#define MODE_FLAG_DIRECTED (1<<2)
+#define MODE_FLAG_BROADCAST (1<<3)
+#define MODE_FLAG_MULTICAST (1<<4)
+
+/* CDC Spec class requests - CDC Spec Table 46 */
+#define SET_ETHERNET_MULTICAST_FILTER 0x40
+#define SET_ETHERNET_PACKET_FILTER 0x43
+
+#define HSO_SERIAL_FLAG_THROTTLED 0
+#define HSO_SERIAL_FLAG_TX_SENT 1
+#define HSO_SERIAL_FLAG_RX_SENT 2
+#define HSO_SERIAL_USB_GONE 3
+
+#define HSO_SERIAL_MAGIC 0x48534f31
+
+/* Number of ttys to handle */
+#define HSO_SERIAL_TTY_MINORS 256
+
+#define MAX_RX_URBS 2
+
+#define get_serial_by_tty(x) \
+ (x ? (struct hso_serial *)x->driver_data : 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;
+ spinlock_t shared_int_lock;
+};
+
+struct hso_net {
+ struct hso_device *parent;
+ struct net_device_stats stats;
+ struct net_device *net;
+
+ 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;
+ struct ethhdr dummy_eth_head;
+
+ __u16 bcdCDC;
+ __u16 wMaxSegmentSize;
+ __u16 wNumberMCFilters;
+ __u16 mode_flags;
+ unsigned long flags;
+
+};
+
+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;
+ spinlock_t buffer_lock;
+ struct usb_ctrlrequest ctrl_req_tx;
+ struct usb_ctrlrequest ctrl_req_rx;
+
+ struct usb_endpoint_descriptor *in_endp;
+ struct usb_endpoint_descriptor *out_endp;
+
+ unsigned long flags;
+ u8 rts_state;
+ u8 dtr_state;
+
+ /* from usb_serial_port */
+ struct tty_struct *tty;
+ int open_count;
+ spinlock_t serial_lock;
+ void *private;
+
+ int (*write_data) (struct hso_serial *serial);
+};
+
+struct hso_device {
+ union {
+ struct hso_serial *dev_serial;
+ struct hso_net *dev_net;
+ } port_data;
+
+ u32 port_spec;
+
+ u8 is_active;
+ u8 suspend_disabled;
+ struct work_struct async_get_intf;
+ struct work_struct async_put_intf;
+
+ struct usb_device *usb;
+ struct usb_interface *interface;
+
+ struct device *dev;
+ struct kref ref;
+
+ /* TODO: Not sure about the ones below */
+ struct proc_dir_entry *ourproc;
+};
+
+/* 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 */
+/*****************************************************************************/
+/* Network interface functions */
+static int hso_net_open(struct net_device *net);
+static int hso_net_close(struct net_device *net);
+static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net);
+static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd);
+static struct net_device_stats *hso_net_get_stats(struct net_device *net);
+static void hso_net_tx_timeout(struct net_device *net);
+static void hso_net_set_multicast(struct net_device *net);
+static void read_bulk_callback(struct urb *urb);
+static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
+ unsigned int count, unsigned char is_eop);
+static void write_bulk_callback(struct urb *urb);
+/* 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(struct urb *urb);
+static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial);
+static void hso_std_serial_read_bulk_callback(struct urb *urb);
+static void hso_std_serial_write_bulk_callback(struct urb *urb);
+static void _hso_serial_set_termios(struct tty_struct *tty,
+ struct ktermios *old);
+static void hso_kick_transmit(struct hso_serial *serial);
+/* Base driver functions */
+static int hso_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void hso_disconnect(struct usb_interface *interface);
+/* Helper functions */
+static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int,
+ struct usb_device *usb, gfp_t gfp);
+static void hso_net_init(struct net_device *net);
+static void set_ethernet_addr(struct hso_net *odev);
+static struct hso_serial *get_serial_by_index(unsigned index);
+static struct hso_serial *get_serial_by_shared_int_and_type(
+ struct hso_shared_int *shared_int,
+ int mux);
+static int get_free_serial_index(void);
+static void set_serial_by_index(unsigned index, struct hso_serial *serial);
+static int remove_net_device(struct hso_device *hso_dev);
+static int add_net_device(struct hso_device *hso_dev);
+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);
+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 int hso_suspend(struct usb_interface *iface, pm_message_t message);
+static int hso_resume(struct usb_interface *iface);
+static 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);
+
+/*****************************************************************************/
+/* Helping functions */
+/*****************************************************************************/
+
+/* convert a character representing a hex value to a number */
+static unsigned char hex2dec(unsigned char digit)
+{
+
+ if ((digit >= '0') && (digit <= '9'))
+ return (digit - '0');
+ /* Map all characters to 0-15 */
+ if ((digit >= 'a') && (digit <= 'z'))
+ return (digit - 'a' + 10) % 16;
+ if ((digit >= 'A') && (digit <= 'Z'))
+ return (digit - 'A' + 10) % 16;
+ return 16;
+}
+
+#define SIOCSETSUSPEND (SIOCDEVPRIVATE+3)
+#define SIOCSETRADIO (SIOCDEVPRIVATE+4)
+
+/* #define DEBUG */
+
+#define dev2net(x) (x->port_data.dev_net)
+#define dev2ser(x) (x->port_data.dev_serial)
+
+/* Debugging functions */
+#ifdef DEBUG
+static void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
+ unsigned int len)
+{
+ u8 i = 0;
+
+ printk(KERN_DEBUG "[%d:%s]: len %d", line_count, func_name, len);
+
+ for (i = 0; i < len; i++) {
+ if (!(i % 16))
+ printk("\n 0x%03x: ", i);
+ printk("%02x ", (unsigned char)buf[i]);
+ }
+ printk("\n");
+}
+
+#define DUMP(buf_, len_) \
+ dbg_dump(__LINE__, __func__, buf_, len_)
+
+#define DUMP1(buf_, len_) \
+ do { \
+ if (0x01 & debug) \
+ DUMP(buf_, len_); \
+ } while (0)
+#else
+#define DUMP(buf_, len_)
+#define DUMP1(buf_, len_)
+#endif
+
+/* module parameters */
+static int debug;
+static int procfs = 1;
+static int tty_major;
+static int disable_net;
+static int enable_autopm;
+
+/* driver info */
+static const char driver_name[] = "hso";
+static const char tty_filename[] = "ttyHS";
+static const char *version = __FILE__ ": " DRIVER_VERSION " " MOD_AUTHOR;
+/* the usb driver itself (registered in hso_init) */
+static struct usb_driver hso_driver;
+/* the procfs vars (if procfs enabled as parameter) */
+static struct proc_dir_entry *hso_proc_dir;
+static struct proc_dir_entry *hso_proc_dir_devices;
+/* serial structures */
+static struct tty_driver *tty_drv;
+static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
+static struct hso_device *network_table[HSO_MAX_NET_DEVICES];
+static spinlock_t serial_table_lock;
+static struct ktermios *hso_serial_termios[HSO_SERIAL_TTY_MINORS];
+static struct ktermios *hso_serial_termios_locked[HSO_SERIAL_TTY_MINORS];
+
+static const s32 default_port_spec[] = {
+ HSO_INTF_MUX | HSO_PORT_NETWORK,
+ HSO_INTF_BULK | HSO_PORT_DIAG,
+ HSO_INTF_BULK | HSO_PORT_MODEM,
+ 0
+};
+
+static const s32 icon321_port_spec[] = {
+ HSO_INTF_MUX | HSO_PORT_NETWORK,
+ HSO_INTF_BULK | HSO_PORT_DIAG2,
+ HSO_INTF_BULK | HSO_PORT_MODEM,
+ HSO_INTF_BULK | HSO_PORT_DIAG,
+ 0
+};
+
+#define default_port_device(vendor, product) \
+ USB_DEVICE(vendor, product), \
+ .driver_info = (kernel_ulong_t)default_port_spec
+
+#define icon321_port_device(vendor, product) \
+ USB_DEVICE(vendor, product), \
+ .driver_info = (kernel_ulong_t)icon321_port_spec
+
+/* list of devices we support */
+static struct usb_device_id hso_ids[] = {
+ {default_port_device(0x0af0, 0x6711)},
+ {default_port_device(0x0af0, 0x6731)},
+ {default_port_device(0x0af0, 0x6751)},
+ {default_port_device(0x0af0, 0x6771)},
+ {default_port_device(0x0af0, 0x6791)},
+ {default_port_device(0x0af0, 0x6811)},
+ {default_port_device(0x0af0, 0x6911)},
+ {default_port_device(0x0af0, 0x6951)},
+ {default_port_device(0x0af0, 0x6971)},
+ {default_port_device(0x0af0, 0x7011)},
+ {default_port_device(0x0af0, 0x7031)},
+ {default_port_device(0x0af0, 0x7051)},
+ {default_port_device(0x0af0, 0x7071)},
+ {default_port_device(0x0af0, 0x7111)},
+ {default_port_device(0x0af0, 0x7211)},
+ {default_port_device(0x0af0, 0x7251)},
+ {default_port_device(0x0af0, 0x7271)},
+ {default_port_device(0x0af0, 0x7311)},
+ {default_port_device(0x0af0, 0xc031)}, /* Icon-Edge */
+ {icon321_port_device(0x0af0, 0xd013)}, /* Module HSxPA */
+ {icon321_port_device(0x0af0, 0xd031)}, /* Icon-321 */
+ {default_port_device(0x0af0, 0xd033)}, /* Icon-322 */
+ {USB_DEVICE(0x0af0, 0x7301)}, /* GE40x */
+ {USB_DEVICE(0x0af0, 0x7361)}, /* GE40x */
+ {USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */
+ {USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */
+ {USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, hso_ids);
+
+/* driver setup */
+static struct usb_driver hso_driver = {
+ .name = driver_name,
+ .probe = hso_probe,
+ .disconnect = hso_disconnect,
+ .id_table = hso_ids,
+ .suspend = hso_suspend,
+ .resume = hso_resume,
+ .supports_autosuspend = 1,
+};
+
+/* Sysfs attribute & function declaration */
+static ssize_t hso_sysfs_show_porttype(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL);
+
+static ssize_t hso_sysfs_show_porttype(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hso_device *hso_dev = dev->driver_data;
+ char *port_name;
+
+ if (!hso_dev)
+ return 0;
+
+ switch (hso_dev->port_spec & HSO_PORT_MASK) {
+ case HSO_PORT_CONTROL:
+ port_name = "Control";
+ break;
+ case HSO_PORT_APP:
+ port_name = "Application";
+ break;
+ case HSO_PORT_APP2:
+ port_name = "Application2";
+ break;
+ case HSO_PORT_GPS:
+ port_name = "GPS";
+ break;
+ case HSO_PORT_GPS_CONTROL:
+ port_name = "GPS Control";
+ break;
+ case HSO_PORT_PCSC:
+ port_name = "PCSC";
+ break;
+ case HSO_PORT_DIAG:
+ port_name = "Diagnostic";
+ break;
+ case HSO_PORT_DIAG2:
+ port_name = "Diagnostic2";
+ break;
+ case HSO_PORT_MODEM:
+ port_name = "Modem";
+ break;
+ case HSO_PORT_NETWORK:
+ port_name = "Network";
+ break;
+ default:
+ port_name = "Unknown";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", port_name);
+}
+
+/* Procfs functions */
+
+static int hso_proc_options(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ /* get the module parameters */
+ len +=
+ snprintf(buf + len, count - len, "Version: %s\n", DRIVER_VERSION);
+ len += snprintf(buf + len, count - len, "debug: 0x%02x\n", debug);
+ len += snprintf(buf + len, count - len, "procfs: 0x%02x\n", procfs);
+ len +=
+ snprintf(buf + len, count - len, "tty_major: 0x%02x\n", tty_major);
+ len +=
+ snprintf(buf + len, count - len, "enable_autopm: 0x%02x\n",
+ enable_autopm);
+ len +=
+ snprintf(buf + len, count - len, "disable_net: 0x%02x\n",
+ disable_net);
+ return len;
+}
+
+static int hso_proc_port_info(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct hso_device *hso_dev = (struct hso_device *)data;
+ char *port_name = NULL;
+
+ D1("count: %d", count);
+
+ switch (hso_dev->port_spec & HSO_PORT_MASK) {
+ case HSO_PORT_CONTROL:
+ port_name = "Control";
+ break;
+ case HSO_PORT_APP:
+ port_name = "Application";
+ break;
+ case HSO_PORT_GPS:
+ port_name = "GPS";
+ break;
+ case HSO_PORT_GPS_CONTROL:
+ port_name = "GPS Control";
+ break;
+ case HSO_PORT_APP2:
+ port_name = "Application2";
+ break;
+ case HSO_PORT_PCSC:
+ port_name = "PCSC";
+ break;
+ case HSO_PORT_DIAG:
+ port_name = "Diagnostic";
+ break;
+ case HSO_PORT_DIAG2:
+ port_name = "Diagnostic2";
+ break;
+ case HSO_PORT_MODEM:
+ port_name = "Modem";
+ break;
+ case HSO_PORT_NETWORK:
+ port_name = "Network";
+ break;
+ default:
+ port_name = "Unknown";
+ break;
+ }
+
+ len += snprintf(buf + len, count - len, "%s\n", port_name);
+
+ /* return the device id to the user, for use in scripts for autopm */
+ len +=
+ snprintf(buf + len, count - len, "USB bus ID:\t%s\n",
+ hso_dev->usb->dev.bus_id);
+ return len;
+}
+
+/* Network interface functions */
+
+/* called when net interface is brought up by ifconfig */
+static int hso_net_open(struct net_device *net)
+{
+ struct hso_net *odev = netdev_priv(net);
+ unsigned long flags = 0;
+
+ if (!odev) {
+ dev_err(&net->dev, "No net device !\n");
+ return -ENODEV;
+ }
+ /* reset read counter. */
+ odev->stats.rx_packets = 0;
+ odev->skb_tx_buf = NULL;
+
+ /* setup environment */
+ spin_lock_irqsave(&odev->net_lock, flags);
+ odev->rx_parse_state = WAIT_IP;
+ odev->rx_buf_size = 0;
+ odev->rx_buf_missing = sizeof(struct iphdr);
+ spin_unlock_irqrestore(&odev->net_lock, flags);
+
+ hso_start_net_device(odev->parent);
+
+ /* Tell the kernel we are ready to start receiving from it */
+ netif_start_queue(net);
+
+ /* We are up and running. */
+ set_bit(HSO_NET_RUNNING, &odev->flags);
+
+ return 0;
+}
+
+/* called when interface is brought down by ifconfig */
+static int hso_net_close(struct net_device *net)
+{
+ struct hso_net *odev = netdev_priv(net);
+
+ /* no longer running */
+ clear_bit(HSO_NET_RUNNING, &odev->flags);
+ /* we don't need the queue anymore */
+ netif_stop_queue(net);
+
+ hso_stop_net_device(odev->parent);
+
+ /* done */
+ return 0;
+}
+
+/* called by kernel when we need to transmit a packet */
+static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct hso_net *odev = netdev_priv(net);
+ int result = 0;
+
+ /* Tell the kernel, "No more frames 'til we are done with this one." */
+ netif_stop_queue(net);
+ if (hso_get_activity(odev->parent) == -EAGAIN) {
+ odev->skb_tx_buf = skb;
+ return 0;
+ }
+
+ /* fetch the packet */
+ skb_pull(skb, ETH_HLEN);
+ /* log if asked */
+ DUMP1(skb->data, skb->len);
+ /* Copy it from kernel memory to OUR memory */
+ memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len);
+ D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE);
+
+ /* Fill in the URB for shipping it out. */
+ usb_fill_bulk_urb(odev->mux_bulk_tx_urb,
+ odev->parent->usb,
+ usb_sndbulkpipe(odev->parent->usb,
+ odev->out_endp->
+ bEndpointAddress & 0x7F),
+ odev->mux_bulk_tx_buf, skb->len, write_bulk_callback,
+ odev);
+
+ /* Deal with the Zero Length packet problem, I hope */
+ odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ /* Send the URB on its merry way. */
+ result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC);
+ if (result) {
+ dev_warn(&odev->parent->interface->dev,
+ "failed mux_bulk_tx_urb %d", result);
+ odev->stats.tx_errors++;
+ netif_start_queue(net);
+ } else {
+ odev->stats.tx_packets++;
+ odev->stats.tx_bytes += skb->len;
+ /* And tell the kernel when the last transmit started. */
+ net->trans_start = jiffies;
+ }
+ dev_kfree_skb(skb);
+ /* we're done */
+ return result;
+}
+
+static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+ struct hso_net *odev = netdev_priv(net);
+ u32 usercmd = 0;
+ char tmp[40];
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE + 1:
+ /* Chose this one because SIOCDEVPRIVATE used somewhere else in
+ * this code */
+ /* return the number of sent bytes */
+ D5("Transmitted: %lu", odev->stats.tx_bytes);
+ rq->ifr_ifru.ifru_ivalue = odev->stats.tx_bytes;
+ return 0;
+
+ case SIOCETHTOOL:
+ /* net specific */
+ if (copy_from_user(&usercmd, rq->ifr_data, sizeof(usercmd)))
+ return -EFAULT;
+
+ switch (usercmd) {
+ case ETHTOOL_GDRVINFO:
+ {
+ /* get driver info */
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN);
+ strncpy(info.version, DRIVER_VERSION,
+ ETHTOOL_BUSINFO_LEN);
+ sprintf(tmp, "usb%d:%d",
+ odev->parent->usb->bus->busnum,
+ odev->parent->usb->devnum);
+ strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
+ sprintf(tmp, "%s %x.%x", driver_name,
+ ((odev->bcdCDC & 0xff00) >> 8),
+ (odev->bcdCDC & 0x00ff));
+ strncpy(info.fw_version, tmp, ETHTOOL_BUSINFO_LEN);
+ if (copy_to_user(rq->ifr_data, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case ETHTOOL_GLINK:
+ {
+ /* get link status */
+ struct ethtool_value edata = { ETHTOOL_GLINK };
+
+ edata.data = netif_carrier_ok(net);
+ if (copy_to_user(rq->ifr_data, &edata, sizeof(edata)))
+ return -EFAULT;
+
+ return 0;
+ }
+ default:
+ dev_warn(&net->dev, "Got unsupported ioctl: %x\n",
+ usercmd);
+ /* the ethtool user space tool relies on this */
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -ENOTTY; /* per ioctl man page */
+ }
+}
+
+static struct net_device_stats *hso_net_get_stats(struct net_device *net)
+{
+ return &((struct hso_net *)netdev_priv(net))->stats;
+}
+
+/* called when a packet did not ack after watchdogtimeout */
+static void hso_net_tx_timeout(struct net_device *net)
+{
+ struct hso_net *odev = netdev_priv(net);
+
+ if (!odev)
+ return;
+
+ /* Tell syslog we are hosed. */
+ dev_warn(&net->dev, "Tx timed out.\n");
+
+ /* Tear the waiting frame off the list */
+ if (odev->mux_bulk_tx_urb
+ && (odev->mux_bulk_tx_urb->status == -EINPROGRESS))
+ usb_unlink_urb(odev->mux_bulk_tx_urb);
+
+ /* Update statistics */
+ odev->stats.tx_errors++;
+}
+
+/* setup the multicast filters */
+static void hso_net_set_multicast(struct net_device *net)
+{
+ struct hso_net *odev = netdev_priv(net);
+ int i = 0;
+ __u8 *buf = NULL;
+
+ /* Tell the kernel to stop sending us frames while we get this all set
+ * up. */
+ netif_stop_queue(net);
+
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (net->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ D1("%s: Promiscuous mode enabled", net->name);
+ odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST |
+ MODE_FLAG_PROMISCUOUS;
+ } else if (net->mc_count > odev->wNumberMCFilters) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ D1("%s: too many MC filters for hardware, using allmulti",
+ net->name);
+ odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ } else if (net->flags & IFF_ALLMULTI) {
+ /* Filter in software */
+ D1("%s: using allmulti", net->name);
+ odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ } else {
+ /* do multicast filtering in hardware */
+ struct dev_mc_list *mclist;
+ D1("%s: set multicast filters", net->name);
+ odev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ buf = kmalloc(6 * net->mc_count, GFP_ATOMIC);
+ if (!buf) {
+ dev_err(&net->dev, "No memory to allocate?");
+ goto exit;
+ }
+ for (i = 0, mclist = net->mc_list; mclist && i < net->mc_count;
+ i++, mclist = mclist->next) {
+ memcpy(&mclist->dmi_addr, &buf[i * 6], 6);
+ }
+ kfree(buf);
+ }
+
+exit:
+ /* Tell the kernel to start giving frames to us again. */
+ netif_wake_queue(net);
+}
+
+/* Moving data from usb to kernel (in interrupt state) */
+static void read_bulk_callback(struct urb *urb)
+{
+ struct hso_net *odev = urb->context;
+ struct net_device *net = NULL;
+ int result = 0;
+ unsigned long flags = 0;
+ int status = urb->status;
+
+ /* is al ok? (Filip: Who's Al ?) */
+ if (status) {
+ log_usb_status(status, __func__);
+ return;
+ }
+
+ /* Sanity check */
+ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
+ D1("BULK IN callback but driver is not active!");
+ return;
+ }
+ usb_mark_last_busy(urb->dev);
+
+ net = odev->net;
+
+ if (!netif_device_present(net)) {
+ /* Somebody killed our network interface... */
+ return;
+ }
+
+ if (odev->parent->port_spec & HSO_INFO_CRC_BUG) {
+ u32 rest;
+ u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
+ rest = urb->actual_length % odev->in_endp->wMaxPacketSize;
+ if (((rest == 5) || (rest == 6))
+ && !memcmp(((u8 *) urb->transfer_buffer) +
+ urb->actual_length - 4, crc_check, 4)) {
+ urb->actual_length -= 4;
+ }
+ }
+
+ /* do we even have a packet? */
+ if (urb->actual_length) {
+ /* Handle the IP stream, add header and push it onto network
+ * stack if the packet is complete. */
+ spin_lock_irqsave(&odev->net_lock, flags);
+ packetizeRx(odev, urb->transfer_buffer, urb->actual_length,
+ (urb->transfer_buffer_length >
+ urb->actual_length) ? 1 : 0);
+ spin_unlock_irqrestore(&odev->net_lock, flags);
+ }
+
+ /* We are done with this URB, resubmit it. Prep the USB to wait for
+ * another frame. Reuse same as received. */
+ usb_fill_bulk_urb(urb,
+ odev->parent->usb,
+ usb_rcvbulkpipe(odev->parent->usb,
+ odev->in_endp->
+ bEndpointAddress & 0x7F),
+ urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE,
+ read_bulk_callback, odev);
+
+ /* Give this to the USB subsystem so it can tell us when more data
+ * arrives. */
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_warn(&odev->parent->interface->dev,
+ "%s failed submit mux_bulk_rx_urb %d", __func__,
+ result);
+}
+
+/* make a real packet from the received USB buffer */
+static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt,
+ unsigned int count, unsigned char is_eop)
+{
+ unsigned short temp_bytes = 0;
+ unsigned short buffer_offset = 0;
+ unsigned short frame_len = 0;
+ unsigned char *tmp_rx_buf = NULL;
+ struct ethhdr *eth_head = NULL;
+
+ /* log if needed */
+ D1("Rx %d bytes", count);
+ DUMP(ip_pkt, min(128, (int)count));
+
+ while (count) {
+ switch (odev->rx_parse_state) {
+ case WAIT_IP:
+ /* waiting for IP header. */
+ /* wanted bytes - size of ip header */
+ temp_bytes =
+ (count <
+ odev->rx_buf_missing) ? count : odev->
+ rx_buf_missing;
+
+ memcpy(((unsigned char *)(&odev->rx_ip_hdr)) +
+ odev->rx_buf_size, ip_pkt + buffer_offset,
+ temp_bytes);
+
+ odev->rx_buf_size += temp_bytes;
+ buffer_offset += temp_bytes;
+ odev->rx_buf_missing -= temp_bytes;
+ count -= temp_bytes;
+
+ if (!odev->rx_buf_missing) {
+ /* header is complete allocate an sk_buffer and
+ * continue to WAIT_DATA */
+ frame_len = ntohs(odev->rx_ip_hdr.tot_len);
+
+ if ((frame_len > DEFAULT_MRU) ||
+ (frame_len < sizeof(struct iphdr))) {
+ dev_err(&odev->net->dev,
+ "Invalid frame (%d) length\n",
+ frame_len);
+ odev->rx_parse_state = WAIT_SYNC;
+ continue;
+ }
+ /* Allocate an sk_buff */
+ odev->skb_rx_buf = dev_alloc_skb(
+ frame_len + 2 +
+ odev->net->hard_header_len);
+ if (!odev->skb_rx_buf) {
+ /* We got no receive buffer. */
+ D1("could not allocate memory");
+ odev->rx_parse_state = WAIT_SYNC;
+ return;
+ }
+ /* Here's where it came from */
+ odev->skb_rx_buf->dev = odev->net;
+
+ /* Make some headroom: standard alignment + the
+ * ethernet header. */
+ skb_reserve(odev->skb_rx_buf,
+ 2 + odev->net->hard_header_len);
+
+ /* Copy what we got so far. make room for iphdr
+ * after tail. */
+ tmp_rx_buf =
+ skb_put(odev->skb_rx_buf,
+ sizeof(struct iphdr));
+ memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr),
+ sizeof(struct iphdr));
+
+ /* ETH_HLEN */
+ odev->rx_buf_size =
+ odev->net->hard_header_len +
+ sizeof(struct iphdr);
+
+ /* Filip actually use .tot_len */
+ odev->rx_buf_missing =
+ frame_len - sizeof(struct iphdr);
+ odev->rx_parse_state = WAIT_DATA;
+ }
+ break;
+
+ case WAIT_DATA:
+ temp_bytes = (count < odev->rx_buf_missing)
+ ? count : odev->rx_buf_missing;
+
+ /* Copy the rest of the bytes that are left in the
+ * buffer into the waiting sk_buf. */
+ /* Make room for temp_bytes after tail. */
+ tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes);
+ memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes);
+
+ odev->rx_buf_missing -= temp_bytes;
+ count -= temp_bytes;
+ buffer_offset += temp_bytes;
+ odev->rx_buf_size += temp_bytes;
+ if (!odev->rx_buf_missing) {
+ /* Packet is complete. Inject into stack. */
+ /* Add fake ethernet header. */
+ eth_head = (struct ethhdr *)skb_push(odev->
+ skb_rx_buf,
+ odev->
+ net->
+ hard_header_len);
+ memcpy(eth_head, &odev->dummy_eth_head,
+ sizeof(struct ethhdr));
+
+ /* Not sure here either */
+ odev->skb_rx_buf->protocol = eth_type_trans(
+ odev->skb_rx_buf,
+ odev->net);
+ /* don't check it */
+ odev->skb_rx_buf->ip_summed =
+ CHECKSUM_UNNECESSARY;
+ /* Ship it off to the kernel */
+ netif_rx(odev->skb_rx_buf);
+ /* No longer our buffer. */
+ odev->skb_rx_buf = NULL;
+
+ /* update out statistics */
+ odev->stats.rx_packets++;
+
+ /* Hmmm, wonder if we have received the IP len
+ * or the ETH len. */
+ odev->stats.rx_bytes += odev->rx_buf_size;
+
+ odev->rx_buf_size = 0;
+ odev->rx_buf_missing = sizeof(struct iphdr);
+ odev->rx_parse_state = WAIT_IP;
+ }
+ break;
+
+ case WAIT_SYNC:
+ D1(" W_S");
+ count = 0;
+ break;
+ default:
+ D1(" ");
+ count--;
+ break;
+ }
+ }
+
+ /* Recovery mechanism for WAIT_SYNC state. */
+ if (is_eop) {
+ if (odev->rx_parse_state == WAIT_SYNC) {
+ odev->rx_parse_state = WAIT_IP;
+ odev->rx_buf_size = 0;
+ odev->rx_buf_missing = sizeof(struct iphdr);
+ }
+ }
+}
+
+/* USB tells is xmit done, we should start the netqueue again */
+static void write_bulk_callback(struct urb *urb)
+{
+ struct hso_net *odev = urb->context;
+ int status = urb->status;
+
+ /* Sanity check */
+ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) {
+ dev_err(&urb->dev->dev, "%s: device not running\n", __func__);
+ return;
+ }
+
+ /* Do we still have a valid kernel network device? */
+ if (!netif_device_present(odev->net)) {
+ dev_err(&urb->dev->dev, "%s: net device not present\n",
+ __func__);
+ return;
+ }
+
+ /* log status, but don't act on it, we don't need to resubmit anything
+ * anyhow */
+ if (status)
+ log_usb_status(status, __func__);
+
+ hso_put_activity(odev->parent);
+
+ /* Tell the network interface we are ready for another frame */
+ netif_wake_queue(odev->net);
+}
+
+/* Serial driver functions */
+
+static void _hso_serial_set_termios(struct tty_struct *tty,
+ struct ktermios *old)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+
+ if ((!tty) || (!tty->termios) || (!serial)) {
+ printk(KERN_ERR "%s: no tty structures", __func__);
+ return;
+ }
+
+ D4("port %d", serial->minor);
+
+ /*
+ * The default requirements for this device are:
+ */
+ serial->tty->termios->c_iflag &=
+ ~(IGNBRK /* disable ignore break */
+ | BRKINT /* disable break causes interrupt */
+ | PARMRK /* disable mark parity errors */
+ | ISTRIP /* disable clear high bit of input characters */
+ | INLCR /* disable translate NL to CR */
+ | IGNCR /* disable ignore CR */
+ | ICRNL /* disable translate CR to NL */
+ | IXON); /* disable enable XON/XOFF flow control */
+
+ /* disable postprocess output characters */
+ serial->tty->termios->c_oflag &= ~OPOST;
+
+ serial->tty->termios->c_lflag &=
+ ~(ECHO /* disable echo input characters */
+ | ECHONL /* disable echo new line */
+ | ICANON /* disable erase, kill, werase, and rprnt
+ special characters */
+ | ISIG /* disable interrupt, quit, and suspend special
+ characters */
+ | IEXTEN); /* disable non-POSIX special characters */
+
+ serial->tty->termios->c_cflag &=
+ ~(CSIZE /* no size */
+ | PARENB /* disable parity bit */
+ | CBAUD); /* clear current baud rate */
+
+ serial->tty->termios->c_cflag |=
+ (CS8 /* character size 8 bits */
+ | B115200); /* baud rate 115200 */
+
+ /*
+ * Force low_latency on; otherwise the pushes are scheduled;
+ * this is bad as it opens up the possibility of dropping bytes
+ * on the floor. We don't want to drop bytes on the floor. :)
+ */
+ serial->tty->low_latency = 1;
+
+ /* Notify the tty driver that the termios have changed. */
+ serial->tty->ldisc.set_termios(serial->tty, NULL);
+ return;
+}
+
+/* open the requested serial port */
+static int hso_serial_open(struct tty_struct *tty, struct file *filp)
+{
+ struct hso_serial *serial = get_serial_by_index(tty->index);
+ int result = 0;
+ unsigned long flags;
+
+ /* sanity check */
+ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) {
+ tty->driver_data = NULL;
+ D1("Failed to open port");
+ return -ENODEV;
+ }
+
+ usb_autopm_get_interface(serial->parent->interface);
+
+ D1("Opening %d", serial->minor);
+ /* lock it down */
+ spin_lock_irqsave(&serial->serial_lock, flags);
+
+ kref_get(&serial->parent->ref);
+
+ /* setup */
+ tty->driver_data = serial;
+ serial->tty = tty;
+
+ /* check for port allready opened, if not set the termios */
+ serial->open_count++;
+ if (serial->open_count == 1) {
+ tty->low_latency = 1;
+ serial->flags = 0;
+ /* Force default termio settings */
+ _hso_serial_set_termios(tty, NULL);
+ result = hso_start_serial_device(serial->parent);
+ if (result) {
+ hso_stop_serial_device(serial->parent);
+ serial->open_count--;
+ kref_put(&serial->parent->ref, hso_serial_ref_free);
+ }
+ } else {
+ D1("Port was already open");
+ }
+
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ usb_autopm_put_interface(serial->parent->interface);
+
+ /* done */
+ if (result)
+ hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0);
+ return result;
+}
+
+/* close the requested serial port */
+static void hso_serial_close(struct tty_struct *tty, struct file *filp)
+{
+ struct hso_serial *serial = tty->driver_data;
+ unsigned long flags;
+ u8 usb_gone;
+
+ D1("Closing serial port");
+
+ /* sanity check */
+ if (tty == NULL || serial == NULL) {
+ D1("(tty == NULL || tty->driver_data == NULL)");
+ return;
+ }
+
+ usb_gone = test_bit(HSO_SERIAL_USB_GONE, &serial->flags);
+
+ if (!usb_gone)
+ usb_autopm_get_interface(serial->parent->interface);
+
+ /* reset the rts and dtr */
+ /* do the actual close */
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ serial->open_count--;
+ if (serial->open_count <= 0) {
+ kref_put(&serial->parent->ref, hso_serial_ref_free);
+ serial->open_count = 0;
+ if (serial->tty) {
+ serial->tty->driver_data = NULL;
+ serial->tty = NULL;
+ }
+ if (!usb_gone)
+ hso_stop_serial_device(serial->parent);
+ }
+ if (serial->open_count < 0)
+ serial->open_count = 0;
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+ if (!usb_gone)
+ usb_autopm_put_interface(serial->parent->interface);
+}
+
+/* close the requested serial port */
+static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ int space = 0, tx_bytes = 0;
+ unsigned long flags;
+
+ /* sanity check */
+ if (serial == NULL) {
+ printk(KERN_ERR "%s: tty or tty->driver_data is NULL\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+
+ space = serial->tx_data_length - serial->tx_buffer_count;
+ tx_bytes = (count < space) ? count : space;
+
+ if (!tx_bytes)
+ goto out;
+
+ memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes);
+ serial->tx_buffer_count += tx_bytes;
+
+out:
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ hso_kick_transmit(serial);
+ /* done */
+ return tx_bytes;
+}
+
+/* how much room is there for writing */
+static int hso_serial_write_room(struct tty_struct *tty)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ int room = 0;
+ unsigned long flags;
+
+ /* sanity check */
+ if (serial == NULL)
+ return 0;
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ room = serial->tx_data_length - serial->tx_buffer_count;
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ /* return free room */
+ return room;
+}
+
+/* setup the term */
+static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ unsigned long flags;
+
+ /* sanity check */
+ if ((!serial) || (!tty->termios)) {
+ D1("no tty structures");
+ return;
+ }
+
+ if (old)
+ D5("Termios called with: cflags new[%d] - old[%d]",
+ tty->termios->c_cflag, old->c_cflag);
+
+ /* the actual setup */
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ if (serial->open_count)
+ _hso_serial_set_termios(tty, old);
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ /* done */
+ return;
+}
+
+/* how many characters in the buffer */
+static int hso_serial_chars_in_buffer(struct tty_struct *tty)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ int chars = 0;
+ unsigned long flags;
+
+ /* sanity check */
+ if (serial == NULL)
+ return 0;
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ chars = serial->tx_buffer_count;
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ return chars;
+}
+
+static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ unsigned int value = 0;
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ unsigned long flags;
+
+ /* sanity check */
+ if (!serial) {
+ D1("no tty structures");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ value = ((serial->rts_state) ? TIOCM_RTS : 0) |
+ ((serial->dtr_state) ? TIOCM_DTR : 0);
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ return value;
+}
+
+static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ int val = 0;
+ unsigned long flags;
+ int if_num;
+ struct hso_serial *serial = get_serial_by_tty(tty);
+
+ /* sanity check */
+ if (!serial) {
+ D1("no tty structures");
+ return -EINVAL;
+ }
+ if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber;
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ if (set & TIOCM_RTS)
+ serial->rts_state = 1;
+ if (set & TIOCM_DTR)
+ serial->dtr_state = 1;
+
+ if (clear & TIOCM_RTS)
+ serial->rts_state = 0;
+ if (clear & TIOCM_DTR)
+ serial->dtr_state = 0;
+
+ if (serial->dtr_state)
+ val |= 0x01;
+ if (serial->rts_state)
+ val |= 0x02;
+
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+
+ return usb_control_msg(serial->parent->usb,
+ usb_rcvctrlpipe(serial->parent->usb, 0), 0x22,
+ 0x21, val, if_num, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+}
+
+/* Toggles suspend on or off ( used by ioctl ) */
+static int hso_set_suspend(struct hso_device *hso_dev, int enabled)
+{
+ if (!hso_dev)
+ return -ENODEV;
+
+ if (enabled) {
+ if (hso_dev->suspend_disabled) {
+ usb_autopm_put_interface(hso_dev->interface);
+ hso_dev->suspend_disabled = 0;
+ }
+ } else {
+ if (!hso_dev->suspend_disabled) {
+ usb_autopm_get_interface(hso_dev->interface);
+ hso_dev->suspend_disabled = 1;
+ }
+ }
+ return 0;
+}
+
+/* Toggles radioon or off ( used by ioctl ) */
+static int hso_set_radio(struct hso_device *hso_dev, int enabled)
+{
+ if (!hso_dev)
+ return -ENODEV;
+
+ return usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0),
+ enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+}
+
+/* ioctl not supported */
+static int hso_serial_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hso_serial *serial = get_serial_by_tty(tty);
+ int ret = 0;
+ D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
+
+ if (!serial)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOCSETSUSPEND:
+ if (arg)
+ hso_set_suspend(serial->parent, 1);
+ else
+ hso_set_suspend(serial->parent, 0);
+ ret = 0;
+ break;
+ case SIOCSETRADIO:
+ if (arg)
+ hso_set_radio(serial->parent, 1);
+ else
+ hso_set_radio(serial->parent, 0);
+ ret = 0;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ return ret;
+}
+
+static void hso_serial_throttle(struct tty_struct *tty)
+{
+ D1(" ");
+}
+
+static void hso_serial_unthrottle(struct tty_struct *tty)
+{
+ D1(" ");
+}
+
+static void hso_serial_break(struct tty_struct *tty, int break_state)
+{
+ D1(" ");
+}
+
+static int hso_serial_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ return 0;
+}
+
+static void hso_serial_hangup(struct tty_struct *tty)
+{
+ D1("hang up");
+}
+
+/* starts a transmit */
+static void hso_kick_transmit(struct hso_serial *serial)
+{
+ u8 *temp = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serial->serial_lock, flags);
+ if (!serial->tx_buffer_count)
+ goto out;
+
+ if (serial->tx_urb->status == -EINPROGRESS)
+ goto out;
+
+ /* Wakeup USB interface if necessary */
+ if (hso_get_activity(serial->parent) == -EAGAIN)
+ goto out;
+
+ /* Switch pointers around to avoid memcpy */
+ temp = serial->tx_buffer;
+ serial->tx_buffer = serial->tx_data;
+ serial->tx_data = temp;
+ serial->tx_data_count = serial->tx_buffer_count;
+ serial->tx_buffer_count = 0;
+
+out:
+ /* If temp is set, it means we switched buffers */
+ if (temp && serial->write_data)
+ serial->write_data(serial);
+ spin_unlock_irqrestore(&serial->serial_lock, flags);
+}
+
+/* converts mux value to a port spec value */
+static u32 hso_mux_to_port(int mux)
+{
+ u32 result;
+
+ switch (mux) {
+ case 0x1:
+ result = HSO_PORT_CONTROL;
+ break;
+ case 0x2:
+ result = HSO_PORT_APP;
+ break;
+ case 0x4:
+ result = HSO_PORT_PCSC;
+ break;
+ case 0x8:
+ result = HSO_PORT_GPS;
+ break;
+ case 0x10:
+ result = HSO_PORT_APP2;
+ break;
+ default:
+ result = HSO_PORT_NO_PORT;
+ }
+ return result;
+}
+
+/* converts port spec value to a mux value */
+static u32 hso_port_to_mux(int port)
+{
+ u32 result;
+
+ switch (port & HSO_PORT_MASK) {
+ case HSO_PORT_CONTROL:
+ result = 0x0;
+ break;
+ case HSO_PORT_APP:
+ result = 0x1;
+ break;
+ case HSO_PORT_PCSC:
+ result = 0x2;
+ break;
+ case HSO_PORT_GPS:
+ result = 0x3;
+ break;
+ case HSO_PORT_APP2:
+ result = 0x4;
+ break;
+ default:
+ result = 0x0;
+ }
+ return result;
+}
+
+/* make a request (for reading and writing data to muxed serial port) */
+static int mux_device_request(struct hso_serial *serial, u8 type, u16 port,
+ struct urb *ctrl_urb,
+ struct usb_ctrlrequest *ctrl_req,
+ u8 *ctrl_urb_data, u32 size)
+{
+ int result = 0;
+ int pipe = -1;
+
+ /* Sanity check */
+ if (!serial || !ctrl_urb || !ctrl_req) {
+ printk(KERN_ERR "%s: Wrong arguments\n", __func__);
+ return -EINVAL;
+ }
+
+ /* initialize */
+ ctrl_req->wValue = 0;
+ ctrl_req->wIndex = hso_port_to_mux(port);
+ ctrl_req->wLength = size;
+
+ if (type == GET_ENCAPSULATED_RESPONSE) {
+ /* Reading command */
+ ctrl_req->bRequestType = USB_DIR_IN |
+ USB_TYPE_OPTION_VENDOR |
+ USB_RECIP_INTERFACE;
+ ctrl_req->bRequest = GET_ENCAPSULATED_RESPONSE;
+ pipe = usb_rcvctrlpipe(serial->parent->usb, 0);
+ } else {
+ /* Writing command */
+ ctrl_req->bRequestType = USB_DIR_OUT |
+ USB_TYPE_OPTION_VENDOR |
+ USB_RECIP_INTERFACE;
+ ctrl_req->bRequest = SEND_ENCAPSULATED_COMMAND;
+ pipe = usb_sndctrlpipe(serial->parent->usb, 0);
+ }
+ /* syslog */
+ D2("%s command (%02x) len: %d, port: %d",
+ type == GET_ENCAPSULATED_RESPONSE ? "Read" : "Write",
+ ctrl_req->bRequestType, ctrl_req->wLength, port);
+
+ /* Load ctrl urb */
+ ctrl_urb->transfer_flags = 0;
+ usb_fill_control_urb(ctrl_urb,
+ serial->parent->usb,
+ pipe,
+ (u8 *) ctrl_req,
+ ctrl_urb_data, size, ctrl_callback, serial);
+ /* Send it on merry way */
+ result = usb_submit_urb(ctrl_urb, GFP_ATOMIC);
+ if (result) {
+ dev_err(&ctrl_urb->dev->dev,
+ "%s failed submit ctrl_urb %d type %d", __func__,
+ result, type);
+ return result;
+ }
+
+ /* done */
+ return size;
+}
+
+/* called by intr_callback when read occurs */
+static int hso_mux_serial_read(struct hso_serial *serial)
+{
+ if (!serial)
+ return -EINVAL;
+
+ /* clean data */
+ memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE);
+ /* make the request */
+
+ if (serial->num_rx_urbs != 1) {
+ dev_err(&serial->parent->interface->dev,
+ "ERROR: mux'd reads with multiple buffers "
+ "not possible\n");
+ return 0;
+ }
+ return mux_device_request(serial,
+ GET_ENCAPSULATED_RESPONSE,
+ serial->parent->port_spec & HSO_PORT_MASK,
+ serial->rx_urb[0],
+ &serial->ctrl_req_rx,
+ serial->rx_data[0], serial->rx_data_length);
+}
+
+/* used for muxed serial port callback (muxed serial read) */
+static void intr_callback(struct urb *urb)
+{
+ struct hso_shared_int *shared_int = urb->context;
+ struct hso_serial *serial = NULL;
+ unsigned char *port_req = NULL;
+ int status = urb->status;
+ int i = 0;
+
+ usb_mark_last_busy(urb->dev);
+
+ /* sanity check */
+ if (!shared_int)
+ return;
+
+ /* status check */
+ if (status) {
+ log_usb_status(status, __func__);
+ return;
+ }
+ D4("\n--- Got intr callback 0x%02X ---", status);
+
+ /* what request? */
+ port_req = urb->transfer_buffer;
+ D4(" port_req = 0x%.2X\n", *port_req);
+ /* loop over all muxed ports to find the one sending this */
+ for (i = 0; i < 8; i++) {
+ /* max 8 channels on MUX */
+ if (*port_req & (1 << i)) {
+ serial = get_serial_by_shared_int_and_type(shared_int,
+ (1 << i));
+ if (serial != NULL) {
+ D1("Pending read interrupt on port %d\n", i);
+ if (!test_and_set_bit(HSO_SERIAL_FLAG_RX_SENT,
+ &serial->flags)) {
+ /* Setup and send a ctrl req read on
+ * port i */
+ hso_mux_serial_read(serial);
+ } else {
+ D1("Already pending a read on "
+ "port %d\n", i);
+ }
+ }
+ }
+ }
+ /* Resubmit interrupt urb */
+ hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_KERNEL);
+}
+
+/* called for writing to muxed serial port */
+static int hso_mux_serial_write_data(struct hso_serial *serial)
+{
+ if (NULL == serial)
+ return -EINVAL;
+
+ return mux_device_request(serial,
+ SEND_ENCAPSULATED_COMMAND,
+ serial->parent->port_spec & HSO_PORT_MASK,
+ serial->tx_urb,
+ &serial->ctrl_req_tx,
+ serial->tx_data, serial->tx_data_count);
+}
+
+/* called for writing diag or CS serial port */
+static int hso_std_serial_write_data(struct hso_serial *serial)
+{
+ int count = serial->tx_data_count;
+ int result = 0;
+
+ usb_fill_bulk_urb(serial->tx_urb,
+ serial->parent->usb,
+ usb_sndbulkpipe(serial->parent->usb,
+ serial->out_endp->
+ bEndpointAddress & 0x7F),
+ serial->tx_data, serial->tx_data_count,
+ hso_std_serial_write_bulk_callback, serial);
+
+ result = usb_submit_urb(serial->tx_urb, GFP_KERNEL);
+ if (result) {
+ dev_warn(&serial->parent->usb->dev,
+ "Failed to submit urb - res %d\n", result);
+ return result;
+ }
+
+ return count;
+}
+
+/* callback after read or write on muxed serial port */
+static void ctrl_callback(struct urb *urb)
+{
+ struct hso_serial *serial = urb->context;
+ struct usb_ctrlrequest *req = NULL;
+ int status = urb->status;
+
+ /* sanity check */
+ if (!serial)
+ return;
+
+ if (status) {
+ log_usb_status(status, __func__);
+ return;
+ }
+
+ /* what request? */
+ req = (struct usb_ctrlrequest *)(urb->setup_packet);
+ D4("\n--- Got muxed ctrl callback 0x%02X ---", status);
+ D4("Actual length of urb = %d\n", urb->actual_length);
+ DUMP1(urb->transfer_buffer, urb->actual_length);
+
+ if (req->bRequestType ==
+ (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) {
+ /* response to a read command */
+ if (serial->open_count > 0) {
+ /* handle RX data the normal way */
+ put_rxbuf_data(urb, serial);
+ }
+
+ /* Re issue a read as long as we receive data. */
+ if (urb->actual_length != 0)
+ hso_mux_serial_read(serial);
+ else
+ clear_bit(HSO_SERIAL_FLAG_RX_SENT, &serial->flags);
+ } else {
+ hso_put_activity(serial->parent);
+ tty_wakeup(serial->tty);
+ /* response to a write command */
+ hso_kick_transmit(serial);
+ }
+}
+
+/* handle RX data for serial port */
+static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial)
+{
+ struct tty_struct *tty = serial->tty;
+
+ /* Sanity check */
+ if (urb == NULL || serial == NULL) {
+ D1("serial = NULL");
+ return;
+ }
+
+ /* Push data to tty */
+ if (tty && urb->actual_length) {
+ D1("data to push to tty");
+ tty_buffer_request_room(tty, urb->actual_length);
+ tty_insert_flip_string(tty, urb->transfer_buffer,
+ urb->actual_length);
+ tty_flip_buffer_push(tty);
+ }
+}
+
+/* read callback for Diag and CS port */
+static void hso_std_serial_read_bulk_callback(struct urb *urb)
+{
+ struct hso_serial *serial = urb->context;
+ int result = 0;
+ int status = urb->status;
+
+ /* sanity check */
+ if (!serial) {
+ D1("serial == NULL");
+ return;
+ } else if (status) {
+ log_usb_status(status, __func__);
+ return;
+ }
+
+ D4("\n--- Got serial_read_bulk callback %02x ---", status);
+ D1("Actual length = %d\n", urb->actual_length);
+ DUMP1(urb->transfer_buffer, urb->actual_length);
+
+ /* Anyone listening? */
+ if (serial->open_count == 0)
+ return;
+
+ if (status == 0) {
+ if (serial->parent->port_spec & HSO_INFO_CRC_BUG) {
+ u32 rest;
+ u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
+ rest =
+ urb->actual_length %
+ serial->in_endp->wMaxPacketSize;
+ if (((rest == 5) || (rest == 6))
+ && !memcmp(((u8 *) urb->transfer_buffer) +
+ urb->actual_length - 4, crc_check, 4)) {
+ urb->actual_length -= 4;
+ }
+ }
+ /* Valid data, handle RX data */
+ put_rxbuf_data(urb, serial);
+ } else if (status == -ENOENT || status == -ECONNRESET) {
+ /* Unlinked - check for throttled port. */
+ D2("Port %d, successfully unlinked urb", serial->minor);
+ } else {
+ D2("Port %d, status = %d for read urb", serial->minor, status);
+ return;
+ }
+
+ usb_mark_last_busy(urb->dev);
+
+ /* We are done with this URB, resubmit it. Prep the USB to wait for
+ * another frame */
+ usb_fill_bulk_urb(urb, serial->parent->usb,
+ usb_rcvbulkpipe(serial->parent->usb,
+ serial->in_endp->
+ bEndpointAddress & 0x7F),
+ urb->transfer_buffer, serial->rx_data_length,
+ hso_std_serial_read_bulk_callback, serial);
+ /* Give this to the USB subsystem so it can tell us when more data
+ * arrives. */
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result) {
+ dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d",
+ __func__, result);
+ }
+}
+
+/* write callback for Diag and CS port */
+static void hso_std_serial_write_bulk_callback(struct urb *urb)
+{
+ struct hso_serial *serial = urb->context;
+ int status = urb->status;
+
+ /* sanity check */
+ if (!serial) {
+ D1("serial == NULL");
+ return;
+ }
+
+ if (status) {
+ log_usb_status(status, __func__);
+ return;
+ }
+ hso_put_activity(serial->parent);
+ tty_wakeup(serial->tty);
+ hso_kick_transmit(serial);
+
+ D1(" ");
+ return;
+}
+
+/* Base driver functions */
+
+/* operations setup of the serial interface */
+static struct tty_operations hso_serial_ops = {
+ .open = hso_serial_open,
+ .close = hso_serial_close,
+ .write = hso_serial_write,
+ .write_room = hso_serial_write_room,
+ .ioctl = hso_serial_ioctl,
+ .set_termios = hso_serial_set_termios,
+ .throttle = hso_serial_throttle,
+ .unthrottle = hso_serial_unthrottle,
+ .break_ctl = hso_serial_break,
+ .chars_in_buffer = hso_serial_chars_in_buffer,
+ .read_proc = hso_serial_read_proc,
+ .hangup = hso_serial_hangup,
+ .tiocmget = hso_serial_tiocmget,
+ .tiocmset = hso_serial_tiocmset,
+};
+
+static int __init hso_init(void)
+{
+ int i = 0;
+ int result = 0;
+
+ /* put it in the log */
+ printk(KERN_INFO "hso: %s\n", version);
+
+ /* if procfs is enabled, initialize devices */
+ if (procfs) {
+ hso_proc_dir = proc_mkdir(driver_name, NULL);
+ if (hso_proc_dir) {
+ hso_proc_dir_devices =
+ proc_mkdir("devices", hso_proc_dir);
+ create_proc_read_entry("options", 0, hso_proc_dir,
+ hso_proc_options, NULL);
+ }
+ }
+
+ /* Initialise the serial table semaphore and table */
+ spin_lock_init(&serial_table_lock);
+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
+ serial_table[i] = NULL;
+
+ /* allocate our driver using the proper amount of supported minors */
+ tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS);
+ if (!tty_drv)
+ return -ENOMEM;
+
+ /* fill in all needed values */
+ tty_drv->magic = TTY_DRIVER_MAGIC;
+ tty_drv->owner = THIS_MODULE;
+ tty_drv->driver_name = driver_name;
+ tty_drv->name = tty_filename;
+
+ /* if major number is provided as parameter, use that one */
+ if (tty_major)
+ tty_drv->major = tty_major;
+
+ tty_drv->minor_start = 0;
+ tty_drv->num = HSO_SERIAL_TTY_MINORS;
+ tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+ tty_drv->subtype = SERIAL_TYPE_NORMAL;
+ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_drv->init_termios = tty_std_termios;
+ tty_drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ tty_drv->termios = hso_serial_termios;
+ tty_drv->termios_locked = hso_serial_termios_locked;
+ tty_set_operations(tty_drv, &hso_serial_ops);
+
+ /* register the tty driver */
+ result = tty_register_driver(tty_drv);
+ if (result) {
+ printk(KERN_ERR "%s - tty_register_driver failed(%d)\n",
+ __func__, result);
+ return result;
+ }
+
+ /* register this module as an usb driver */
+ result = usb_register(&hso_driver);
+ if (result) {
+ printk(KERN_ERR "Could not register hso driver? error: %d\n",
+ result);
+ /* cleanup serial interface */
+ tty_unregister_driver(tty_drv);
+ return result;
+ }
+
+ /* done */
+ return 0;
+}
+
+static void __exit hso_exit(void)
+{
+ printk(KERN_INFO "hso: unloaded\n");
+
+ tty_unregister_driver(tty_drv);
+ /* deregister the usb driver */
+ usb_deregister(&hso_driver);
+ /* if procfs is enabled, remove the entries */
+
+ if (procfs) {
+ remove_proc_entry("options", hso_proc_dir);
+ remove_proc_entry("devices", hso_proc_dir);
+ remove_proc_entry(driver_name, NULL);
+ }
+
+}
+
+static void hso_log_port(struct hso_device *hso_dev)
+{
+ char *port_type = NULL;
+ char port_dev[20];
+
+ switch (hso_dev->port_spec & HSO_PORT_MASK) {
+ case HSO_PORT_CONTROL:
+ port_type = "Control";
+ break;
+ case HSO_PORT_APP:
+ port_type = "Application";
+ break;
+ case HSO_PORT_GPS:
+ port_type = "GPS";
+ break;
+ case HSO_PORT_GPS_CONTROL:
+ port_type = "GPS control";
+ break;
+ case HSO_PORT_APP2:
+ port_type = "Application2";
+ break;
+ case HSO_PORT_PCSC:
+ port_type = "PCSC";
+ break;
+ case HSO_PORT_DIAG:
+ port_type = "Diagnostic";
+ break;
+ case HSO_PORT_DIAG2:
+ port_type = "Diagnostic2";
+ break;
+ case HSO_PORT_MODEM:
+ port_type = "Modem";
+ break;
+ case HSO_PORT_NETWORK:
+ port_type = "Network";
+ break;
+ default:
+ port_type = "Unknown";
+ break;
+ }
+ if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
+ sprintf(port_dev, "%s", dev2net(hso_dev)->net->name);
+ } else
+ sprintf(port_dev, "/dev/%s%d", tty_filename,
+ dev2ser(hso_dev)->minor);
+
+ dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n",
+ port_type, port_dev);
+}
+
+static int hso_start_net_device(struct hso_device *hso_dev)
+{
+ int i, result = 0;
+ struct hso_net *hso_net = dev2net(hso_dev);
+
+ if (!hso_net)
+ return -ENODEV;
+
+ /* send URBs for all read buffers */
+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+
+ /* Prep a receive URB */
+ usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i],
+ hso_dev->usb,
+ usb_rcvbulkpipe(hso_dev->usb,
+ hso_net->in_endp->
+ bEndpointAddress & 0x7F),
+ hso_net->mux_bulk_rx_buf_pool[i],
+ MUX_BULK_RX_BUF_SIZE, read_bulk_callback,
+ hso_net);
+
+ /* Put it out there so the device can send us stuff */
+ result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i],
+ GFP_KERNEL);
+ if (result)
+ dev_warn(&hso_dev->usb->dev,
+ "%s failed mux_bulk_rx_urb[%d] %d\n", __func__,
+ i, result);
+ }
+
+ return result;
+}
+
+static int hso_stop_net_device(struct hso_device *hso_dev)
+{
+ int i;
+ struct hso_net *hso_net = dev2net(hso_dev);
+
+ if (!hso_net)
+ return -ENODEV;
+
+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+ if (hso_net->mux_bulk_rx_urb_pool[i]
+ && (hso_net->mux_bulk_rx_urb_pool[i]->status ==
+ -EINPROGRESS))
+ usb_unlink_urb(hso_net->mux_bulk_rx_urb_pool[i]);
+
+ }
+ if (hso_net->mux_bulk_tx_urb
+ && (hso_net->mux_bulk_tx_urb->status == -EINPROGRESS))
+ usb_unlink_urb(hso_net->mux_bulk_tx_urb);
+
+ return 0;
+}
+
+static int hso_start_serial_device(struct hso_device *hso_dev)
+{
+ int i, result = 0;
+ struct hso_serial *serial = dev2ser(hso_dev);
+
+ if (!serial)
+ return -ENODEV;
+
+ /* If it is not the MUX port fill in and submit a bulk urb (already
+ * allocated in hso_serial_start) */
+ if (!(serial->parent->port_spec & HSO_INTF_MUX)) {
+ for (i = 0; i < serial->num_rx_urbs; i++) {
+ usb_fill_bulk_urb(serial->rx_urb[i],
+ serial->parent->usb,
+ usb_rcvbulkpipe(serial->parent->usb,
+ serial->in_endp->
+ bEndpointAddress &
+ 0x7F),
+ serial->rx_data[i],
+ serial->rx_data_length,
+ hso_std_serial_read_bulk_callback,
+ serial);
+ result = usb_submit_urb(serial->rx_urb[i], GFP_KERNEL);
+ if (result) {
+ dev_warn(&serial->parent->usb->dev,
+ "Failed to submit urb - res %d\n",
+ result);
+ break;
+ }
+ }
+ } else {
+ spin_lock_bh(&serial->shared_int->shared_int_lock);
+ if (!serial->shared_int->use_count) {
+ result =
+ hso_mux_submit_intr_urb(serial->shared_int,
+ hso_dev->usb, GFP_KERNEL);
+ }
+ serial->shared_int->use_count++;
+ spin_unlock_bh(&serial->shared_int->shared_int_lock);
+ }
+
+ return result;
+}
+
+static int hso_stop_serial_device(struct hso_device *hso_dev)
+{
+ int i;
+ struct hso_serial *serial = dev2ser(hso_dev);
+
+ if (!serial)
+ return -ENODEV;
+
+ for (i = 0; i < serial->num_rx_urbs; i++) {
+ if (serial->rx_urb[i]
+ && (serial->rx_urb[i]->status == -EINPROGRESS)) {
+ if (in_interrupt())
+ usb_unlink_urb(serial->rx_urb[i]);
+ else
+ usb_kill_urb(serial->rx_urb[i]);
+ }
+ }
+
+ if (serial->tx_urb && (serial->tx_urb->status == -EINPROGRESS)) {
+ if (in_interrupt())
+ usb_unlink_urb(serial->tx_urb);
+ else
+ usb_kill_urb(serial->rx_urb[i]);
+ }
+ if (serial->shared_int) {
+ spin_lock_bh(&serial->shared_int->shared_int_lock);
+ if (serial->shared_int->use_count &&
+ (--serial->shared_int->use_count == 0)) {
+ struct urb *urb;
+
+ urb = serial->shared_int->shared_intr_urb;
+ if ((urb) && (urb->status == -EINPROGRESS)) {
+ if (in_interrupt())
+ usb_unlink_urb(urb);
+ else
+ usb_kill_urb(urb);
+ }
+ }
+ spin_unlock_bh(&serial->shared_int->shared_int_lock);
+ }
+
+ return 0;
+}
+
+static void hso_serial_common_free(struct hso_serial *serial)
+{
+ int i;
+
+ if (serial->parent->dev)
+ device_remove_file(serial->parent->dev, &dev_attr_hsotype);
+
+ tty_unregister_device(tty_drv, serial->minor);
+
+ for (i = 0; i < serial->num_rx_urbs; i++) {
+ /* unlink and free RX URB */
+ usb_free_urb(serial->rx_urb[i]);
+ /* free the RX buffer */
+ kfree(serial->rx_data[i]);
+ }
+
+ /* unlink and free TX URB */
+ usb_free_urb(serial->tx_urb);
+ kfree(serial->tx_data);
+}
+
+static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
+ int rx_size, int tx_size)
+{
+ struct device *dev = NULL;
+ int minor;
+ int i;
+
+ minor = get_free_serial_index();
+ if (minor < 0)
+ goto exit;
+
+ /* register our minor number */
+ serial->parent->dev = tty_register_device(tty_drv, minor,
+ &serial->parent->interface->dev);
+ dev = serial->parent->dev;
+ dev->driver_data = serial->parent;
+ i = device_create_file(dev, &dev_attr_hsotype);
+
+ /* fill in specific data for later use */
+ serial->minor = minor;
+ serial->magic = HSO_SERIAL_MAGIC;
+ spin_lock_init(&serial->serial_lock);
+ serial->num_rx_urbs = num_urbs;
+
+ /* RX , allocate urb and initialize */
+
+ /* prepare our RX buffer */
+ serial->rx_data_length = rx_size;
+ for (i = 0; i < serial->num_rx_urbs; i++) {
+ serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!serial->rx_urb[i]) {
+ dev_err(dev, "Could not allocate urb?\n");
+ goto exit;
+ }
+ serial->rx_urb[i]->transfer_buffer = NULL;
+ serial->rx_urb[i]->transfer_buffer_length = 0;
+ serial->rx_data[i] = kzalloc(serial->rx_data_length,
+ GFP_KERNEL);
+ if (!serial->rx_data[i]) {
+ dev_err(dev, "%s - Out of memory\n", __func__);
+ goto exit;
+ }
+ }
+
+ /* TX , allocate urb and initialize */
+ serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!serial->tx_urb) {
+ dev_err(dev, "Could not allocate urb?\n");
+ goto exit;
+ }
+ serial->tx_urb->transfer_buffer = NULL;
+ serial->tx_urb->transfer_buffer_length = 0;
+ /* prepare our TX buffer */
+ serial->tx_data_count = 0;
+ serial->tx_buffer_count = 0;
+ serial->tx_data_length = tx_size;
+ serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL);
+ if (!serial->tx_data) {
+ dev_err(dev, "%s - Out of memory", __func__);
+ goto exit;
+ }
+ serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL);
+ if (!serial->tx_buffer) {
+ dev_err(dev, "%s - Out of memory", __func__);
+ goto exit;
+ }
+
+ return 0;
+exit:
+ hso_serial_common_free(serial);
+ return -1;
+
+}
+
+/* Frees a general hso device */
+static void hso_free_device(struct hso_device *hso_dev)
+{
+ kfree(hso_dev);
+}
+
+/* Creates a general hso device */
+static struct hso_device *hso_create_device(struct usb_interface *intf,
+ int port_spec)
+{
+ struct hso_device *hso_dev;
+
+ hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC);
+ if (!hso_dev)
+ return NULL;
+
+ hso_dev->port_spec = port_spec;
+ hso_dev->usb = interface_to_usbdev(intf);
+ hso_dev->interface = intf;
+ kref_init(&hso_dev->ref);
+
+ INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
+ INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
+
+ return hso_dev;
+}
+
+/* Frees our network device */
+static void hso_free_net_device(struct hso_device *hso_dev)
+{
+ int i;
+ struct hso_net *hso_net = dev2net(hso_dev);
+
+ if (!hso_net)
+ return;
+
+ if (procfs)
+ remove_proc_entry(hso_net->net->name, hso_proc_dir_devices);
+
+ /* start freeing */
+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+ usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]);
+ kfree(hso_net->mux_bulk_rx_buf_pool[i]);
+ }
+ usb_free_urb(hso_net->mux_bulk_tx_urb);
+ kfree(hso_net->mux_bulk_tx_buf);
+
+ remove_net_device(hso_net->parent);
+
+ if (hso_net->net) {
+ unregister_netdev(hso_net->net);
+ free_netdev(hso_net->net);
+ }
+
+ hso_free_device(hso_dev);
+}
+
+/* Creates our network device */
+static struct hso_device *hso_create_net_device(struct usb_interface *interface)
+{
+ int result, i;
+ struct net_device *net = NULL;
+ struct hso_net *hso_net = NULL;
+ struct hso_device *hso_dev = NULL;
+
+ hso_dev = hso_create_device(interface, HSO_INTF_MUX | HSO_PORT_NETWORK);
+ if (!hso_dev)
+ return NULL;
+
+ /* allocate our network device, then we can put in our private data */
+ /* call hso_net_init to do the basic initialization */
+ net = alloc_netdev(sizeof(struct hso_net), "hso%d", hso_net_init);
+ if (!net) {
+ dev_err(&interface->dev, "Unable to create ethernet device\n");
+ goto exit;
+ }
+
+ hso_net = netdev_priv(net);
+
+ hso_dev->port_data.dev_net = hso_net;
+ hso_net->net = net;
+ hso_net->parent = hso_dev;
+
+ hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
+ USB_DIR_IN);
+ if (!hso_net->in_endp) {
+ dev_err(&interface->dev, "Can't find BULK IN endpoint\n");
+ goto exit;
+ }
+ hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
+ USB_DIR_OUT);
+ if (!hso_net->out_endp) {
+ dev_err(&interface->dev, "Can't find BULK OUT endpoint\n");
+ goto exit;
+ }
+ SET_NETDEV_DEV(net, &interface->dev);
+
+ /* registering our net device */
+ result = register_netdev(net);
+ if (result) {
+ dev_err(&interface->dev, "Failed to register device\n");
+ goto exit;
+ }
+
+ /* start allocating */
+ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) {
+ hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!hso_net->mux_bulk_rx_urb_pool[i]) {
+ dev_err(&interface->dev, "Could not allocate rx urb\n");
+ goto exit;
+ }
+ hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE,
+ GFP_KERNEL);
+ if (!hso_net->mux_bulk_rx_buf_pool[i]) {
+ dev_err(&interface->dev, "Could not allocate rx buf\n");
+ goto exit;
+ }
+ }
+ hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!hso_net->mux_bulk_tx_urb) {
+ dev_err(&interface->dev, "Could not allocate tx urb\n");
+ goto exit;
+ }
+ hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL);
+ if (!hso_net->mux_bulk_tx_buf) {
+ dev_err(&interface->dev, "Could not allocate tx buf\n");
+ goto exit;
+ }
+
+ /* and don't forget the MAC address. */
+ set_ethernet_addr(hso_net);
+
+ add_net_device(hso_dev);
+
+ /* setup the proc dirs and files if needed */
+ if (procfs) {
+ if (!hso_proc_dir_devices)
+ printk(KERN_WARNING "hso: Warning, creating port info "
+ "under root\n");
+ create_proc_read_entry(hso_net->net->name, 0,
+ hso_proc_dir_devices, hso_proc_port_info,
+ hso_dev);
+ }
+
+ hso_log_port(hso_dev);
+
+ return hso_dev;
+exit:
+ hso_free_net_device(hso_dev);
+ return NULL;
+}
+
+/* Frees an AT channel ( goes for both mux and non-mux ) */
+static void hso_free_serial_device(struct hso_device *hso_dev)
+{
+ struct hso_serial *serial = dev2ser(hso_dev);
+ char device_name[20];
+
+ if (!serial)
+ return;
+ set_serial_by_index(serial->minor, NULL);
+
+ if (procfs) {
+ sprintf(device_name, "%s%d", tty_filename, serial->minor);
+ remove_proc_entry(device_name, hso_proc_dir_devices);
+ }
+
+ hso_serial_common_free(serial);
+
+ if (serial->shared_int) {
+ spin_lock_bh(&serial->shared_int->shared_int_lock);
+ if (--serial->shared_int->ref_count == 0)
+ hso_free_shared_int(serial->shared_int);
+ spin_unlock_bh(&serial->shared_int->shared_int_lock);
+ }
+ kfree(serial);
+ hso_free_device(hso_dev);
+}
+
+/* Creates a bulk AT channel */
+static struct hso_device *hso_create_bulk_serial_device(
+ struct usb_interface *interface, int port)
+{
+ struct hso_device *hso_dev = NULL;
+ struct hso_serial *serial = NULL;
+ char device_name[20];
+ int num_urbs;
+
+ hso_dev = hso_create_device(interface, port);
+ if (!hso_dev)
+ return NULL;
+
+ serial = kzalloc(sizeof(*serial), GFP_KERNEL);
+ if (!serial)
+ goto exit;
+
+ serial->parent = hso_dev;
+ hso_dev->port_data.dev_serial = serial;
+
+ if (port & HSO_PORT_MODEM)
+ num_urbs = 2;
+ else
+ num_urbs = 1;
+
+ if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE,
+ BULK_URB_TX_SIZE))
+ goto exit;
+
+ serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK,
+ USB_DIR_IN);
+ if (!serial->in_endp) {
+ dev_err(&interface->dev, "Failed to find BULK IN ep\n");
+ goto exit;
+ }
+
+ if (!
+ (serial->out_endp =
+ hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) {
+ dev_err(&interface->dev, "Failed to find BULK IN ep\n");
+ goto exit;
+ }
+
+ serial->write_data = hso_std_serial_write_data;
+
+ /* and record this serial */
+ set_serial_by_index(serial->minor, serial);
+
+ /* setup the proc dirs and files if needed */
+ if (procfs) {
+ if (!hso_proc_dir_devices)
+ printk(KERN_WARNING "hso: Warning, creating port "
+ "info under root\n");
+ sprintf(device_name, "%s%d", tty_filename, serial->minor);
+ create_proc_read_entry(device_name, 0, hso_proc_dir_devices,
+ hso_proc_port_info, hso_dev);
+ }
+
+ hso_log_port(hso_dev);
+
+ /* done, return it */
+ return hso_dev;
+exit:
+ if (hso_dev && serial)
+ hso_serial_common_free(serial);
+ kfree(serial);
+ hso_free_device(hso_dev);
+ return NULL;
+}
+
+/* Creates a multiplexed AT channel */
+static
+struct hso_device *hso_create_mux_serial_device(struct usb_interface *interface,
+ int port,
+ struct hso_shared_int *mux)
+{
+ struct hso_device *hso_dev = NULL;
+ struct hso_serial *serial = NULL;
+ char device_name[20];
+ int port_spec = 0;
+
+ port_spec |= HSO_INTF_MUX;
+ port_spec &= ~HSO_PORT_MASK;
+
+ port_spec |= hso_mux_to_port(port);
+ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT)
+ return NULL;
+
+ hso_dev = hso_create_device(interface, port_spec);
+ if (!hso_dev)
+ return NULL;
+
+ serial = kzalloc(sizeof(*serial), GFP_KERNEL);
+ if (!serial)
+ goto exit;
+
+ hso_dev->port_data.dev_serial = serial;
+ serial->parent = hso_dev;
+
+ if (hso_serial_common_create
+ (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE))
+ goto exit;
+
+ serial->tx_data_length--;
+ serial->write_data = hso_mux_serial_write_data;
+
+ serial->shared_int = mux;
+ spin_lock_bh(&serial->shared_int->shared_int_lock);
+ serial->shared_int->ref_count++;
+ spin_unlock_bh(&serial->shared_int->shared_int_lock);
+
+ /* and record this serial */
+ set_serial_by_index(serial->minor, serial);
+
+ /* setup the proc dirs and files if needed */
+ if (procfs) {
+ if (!hso_proc_dir_devices)
+ printk(KERN_WARNING "hso: Warning, creating port info "
+ "under root\n");
+ sprintf(device_name, "%s%d", tty_filename, serial->minor);
+ create_proc_read_entry(device_name, 0, hso_proc_dir_devices,
+ hso_proc_port_info, hso_dev);
+ }
+
+ hso_log_port(hso_dev);
+
+ /* done, return it */
+ return hso_dev;
+
+exit:
+ if (serial) {
+ tty_unregister_device(tty_drv, serial->minor);
+ kfree(serial);
+ }
+ if (hso_dev)
+ hso_free_device(hso_dev);
+ return NULL;
+
+}
+
+static void hso_free_shared_int(struct hso_shared_int *mux)
+{
+ usb_free_urb(mux->shared_intr_urb);
+ kfree(mux->shared_intr_buf);
+ kfree(mux);
+}
+
+static
+struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface)
+{
+ struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+
+ if (!mux)
+ return NULL;
+
+ mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT,
+ USB_DIR_IN);
+ if (!mux->intr_endp) {
+ dev_err(&interface->dev, "Can't find INT IN endpoint\n");
+ goto exit;
+ }
+
+ mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mux->shared_intr_urb) {
+ dev_err(&interface->dev, "Could not allocate intr urb?");
+ goto exit;
+ }
+ mux->shared_intr_buf = kzalloc(mux->intr_endp->wMaxPacketSize,
+ GFP_KERNEL);
+ if (!mux->shared_intr_buf) {
+ dev_err(&interface->dev, "Could not allocate intr buf?");
+ goto exit;
+ }
+
+ spin_lock_init(&mux->shared_int_lock);
+
+ return mux;
+
+exit:
+ kfree(mux->shared_intr_buf);
+ usb_free_urb(mux->shared_intr_urb);
+ kfree(mux);
+ return NULL;
+}
+
+/* Gets the port spec for a certain interface */
+static int hso_get_config_data(struct usb_interface *interface)
+{
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+ u8 config_data[17];
+ u32 if_num = interface->altsetting->desc.bInterfaceNumber;
+ s32 result = 0;
+
+ if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ 0x86, 0xC0, 0, 0, config_data, 17,
+ USB_CTRL_SET_TIMEOUT) != 0x11) {
+ return -EIO;
+ }
+
+ switch (config_data[if_num]) {
+ case 0x0:
+ result = 0;
+ break;
+ case 0x1:
+ result = HSO_PORT_DIAG;
+ break;
+ case 0x2:
+ result = HSO_PORT_GPS;
+ break;
+ case 0x3:
+ result = HSO_PORT_GPS_CONTROL;
+ break;
+ case 0x4:
+ result = HSO_PORT_APP;
+ break;
+ case 0x5:
+ result = HSO_PORT_APP2;
+ break;
+ case 0x6:
+ result = HSO_PORT_CONTROL;
+ break;
+ case 0x7:
+ result = HSO_PORT_NETWORK;
+ break;
+ case 0x8:
+ result = HSO_PORT_MODEM;
+ break;
+ case 0x9:
+ result = HSO_PORT_MSD;
+ break;
+ case 0xa:
+ result = HSO_PORT_PCSC;
+ break;
+ case 0xb:
+ result = HSO_PORT_VOICE;
+ break;
+ default:
+ result = 0;
+ }
+
+ if (result)
+ result |= HSO_INTF_BULK;
+
+ if (config_data[16] & 0x1)
+ result |= HSO_INFO_CRC_BUG;
+
+ return result;
+}
+
+/* called once for each interface upon device insertion */
+static int hso_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int mux, i, if_num, port_spec;
+ unsigned char port_mask;
+ struct hso_device *hso_dev = NULL;
+ struct hso_shared_int *shared_int = NULL;
+ struct hso_device *tmp_dev = NULL;
+
+ if_num = interface->altsetting->desc.bInterfaceNumber;
+
+ /* Get the interface/port specification from either driver_info or from
+ * the device itself */
+ if (id->driver_info)
+ port_spec = ((u32 *)(id->driver_info))[if_num];
+ else
+ port_spec = hso_get_config_data(interface);
+
+ if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
+ dev_err(&interface->dev, "Not our interface\n");
+ return -ENODEV;
+ }
+ /* Check if we need to switch to alt interfaces prior to port
+ * configuration */
+ if (interface->num_altsetting > 1)
+ usb_set_interface(interface_to_usbdev(interface), if_num, 1);
+ interface->needs_remote_wakeup = 1;
+
+ /* Allocate new hso device(s) */
+ switch (port_spec & HSO_INTF_MASK) {
+ case HSO_INTF_MUX:
+ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) {
+ /* Create the network device */
+ if (!disable_net) {
+ hso_dev = hso_create_net_device(interface);
+ if (!hso_dev)
+ goto exit;
+ tmp_dev = hso_dev;
+ }
+ }
+
+ if (hso_get_mux_ports(interface, &port_mask))
+ /* TODO: de-allocate everything */
+ goto exit;
+
+ shared_int = hso_create_shared_int(interface);
+ if (!shared_int)
+ goto exit;
+
+ for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) {
+ if (port_mask & i) {
+ hso_dev = hso_create_mux_serial_device(
+ interface, i, shared_int);
+ if (!hso_dev)
+ goto exit;
+ }
+ }
+
+ if (tmp_dev)
+ hso_dev = tmp_dev;
+ break;
+
+ case HSO_INTF_BULK:
+ /* It's a regular bulk interface */
+ if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK)
+ && !disable_net)
+ hso_dev = hso_create_net_device(interface);
+ else
+ hso_dev =
+ hso_create_bulk_serial_device(interface, port_spec);
+ if (!hso_dev)
+ goto exit;
+ break;
+ default:
+ goto exit;
+ }
+
+ usb_driver_claim_interface(&hso_driver, interface, hso_dev);
+
+ /* save our data pointer in this device */
+ usb_set_intfdata(interface, hso_dev);
+
+ if (enable_autopm == 0) {
+ usb_autopm_get_interface(hso_dev->interface);
+ hso_dev->suspend_disabled = 1;
+ }
+
+ /* done */
+ return 0;
+
+exit:
+ hso_free_interface(interface);
+ return -ENODEV;
+}
+
+/* device removed, cleaning up */
+static void hso_disconnect(struct usb_interface *interface)
+{
+ hso_free_interface(interface);
+
+ /* remove reference of our private data */
+ usb_set_intfdata(interface, NULL);
+
+ usb_driver_release_interface(&hso_driver, interface);
+}
+
+static void async_get_intf(struct work_struct *data)
+{
+ struct hso_device *hso_dev =
+ container_of(data, struct hso_device, async_get_intf);
+ usb_autopm_get_interface(hso_dev->interface);
+}
+
+static void async_put_intf(struct work_struct *data)
+{
+ struct hso_device *hso_dev =
+ container_of(data, struct hso_device, async_put_intf);
+ usb_autopm_put_interface(hso_dev->interface);
+}
+
+static int hso_get_activity(struct hso_device *hso_dev)
+{
+ if (hso_dev->usb->state == USB_STATE_SUSPENDED) {
+ if (!hso_dev->is_active) {
+ hso_dev->is_active = 1;
+ schedule_work(&hso_dev->async_get_intf);
+ }
+ }
+
+ if (hso_dev->usb->state != USB_STATE_CONFIGURED)
+ return -EAGAIN;
+
+ usb_mark_last_busy(hso_dev->usb);
+
+ return 0;
+}
+
+static int hso_put_activity(struct hso_device *hso_dev)
+{
+ if (hso_dev->usb->state != USB_STATE_SUSPENDED) {
+ if (hso_dev->is_active) {
+ hso_dev->is_active = 0;
+ schedule_work(&hso_dev->async_put_intf);
+ return -EAGAIN;
+ }
+ }
+ hso_dev->is_active = 0;
+ return 0;
+}
+
+/* called by kernel when we need to suspend device */
+static int hso_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ int i, result = 0;
+
+ /* Stop all serial ports */
+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
+ if (serial_table[i] && (serial_table[i]->interface == iface)) {
+ result = hso_stop_serial_device(serial_table[i]);
+ if (result)
+ goto out;
+ }
+ }
+
+ /* Stop all network ports */
+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
+ if (network_table[i] &&
+ (network_table[i]->interface == iface)) {
+ result = hso_stop_net_device(network_table[i]);
+ if (result)
+ goto out;
+ }
+ }
+
+out:
+ return 0;
+}
+
+/* called by kernel when we need to resume device */
+static int hso_resume(struct usb_interface *iface)
+{
+ int i, result = 0;
+ struct hso_net *hso_net;
+
+ /* Start all serial ports */
+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
+ if (serial_table[i] && (serial_table[i]->interface == iface)) {
+ if (dev2ser(serial_table[i])->open_count) {
+ result =
+ hso_start_serial_device(serial_table[i]);
+ hso_kick_transmit(dev2ser(serial_table[i]));
+ if (result)
+ goto out;
+ }
+ }
+ }
+
+ /* Start all network ports */
+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
+ if (network_table[i] &&
+ (network_table[i]->interface == iface)) {
+ hso_net = dev2net(network_table[i]);
+ /* First transmit any lingering data, then restart the
+ * device. */
+ if (hso_net->skb_tx_buf) {
+ dev_dbg(&iface->dev,
+ "Transmitting lingering data\n");
+ hso_net_start_xmit(hso_net->skb_tx_buf,
+ hso_net->net);
+ }
+ result = hso_start_net_device(network_table[i]);
+ if (result)
+ goto out;
+ }
+ }
+
+out:
+ return result;
+}
+
+static void hso_serial_ref_free(struct kref *ref)
+{
+ struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
+
+ hso_free_serial_device(hso_dev);
+}
+
+static void hso_free_interface(struct usb_interface *interface)
+{
+ int i;
+
+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
+ if (serial_table[i]
+ && (serial_table[i]->interface == interface)) {
+ if (dev2ser(serial_table[i])->tty)
+ tty_hangup(dev2ser(serial_table[i])->tty);
+ set_bit(HSO_SERIAL_USB_GONE,
+ &dev2ser(serial_table[i])->flags);
+ kref_put(&serial_table[i]->ref, hso_serial_ref_free);
+ }
+ }
+
+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
+ if (network_table[i]
+ && (network_table[i]->interface == interface)) {
+ /* hso_stop_net_device doesn't stop the net queue since
+ * traffic needs to start it again when suspended */
+ netif_stop_queue(dev2net(network_table[i])->net);
+ hso_stop_net_device(network_table[i]);
+ hso_free_net_device(network_table[i]);
+ }
+ }
+}
+
+/* Helper functions */
+
+/* Get the endpoint ! */
+static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
+ int type, int dir)
+{
+ int i;
+ struct usb_host_interface *iface = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *endp = NULL;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+ endp = &iface->endpoint[i].desc;
+ if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
+ ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type))
+ return endp;
+ }
+
+ return NULL;
+}
+
+/* Get the byte that describes which ports are enabled */
+static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports)
+{
+ int i;
+ struct usb_host_interface *iface = intf->cur_altsetting;
+
+ if (iface->extralen == 3) {
+ *ports = iface->extra[2];
+ return 0;
+ }
+
+ for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+ if (iface->endpoint[i].extralen == 3) {
+ *ports = iface->endpoint[i].extra[2];
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* interrupt urb needs to be submitted, used for serial read of muxed port */
+static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int,
+ struct usb_device *usb, gfp_t gfp)
+{
+ int result = 0;
+
+ usb_fill_int_urb(shared_int->shared_intr_urb, usb,
+ usb_rcvintpipe(usb,
+ shared_int->intr_endp->bEndpointAddress & 0x7F),
+ shared_int->shared_intr_buf,
+ shared_int->intr_endp->wMaxPacketSize,
+ intr_callback, shared_int,
+ shared_int->intr_endp->bInterval);
+
+ result = usb_submit_urb(shared_int->shared_intr_urb, gfp);
+ if (result)
+ dev_warn(&usb->dev, "%s failed mux_intr_urb %d", __func__,
+ result);
+
+ return result;
+}
+
+/* initialize the network interface */
+static void hso_net_init(struct net_device *net)
+{
+ struct hso_net *hso_net = netdev_priv(net);
+
+ /* initialize to zero */
+ memset(hso_net, 0, sizeof(*hso_net));
+ D1("sizeof hso_net is %d", (int)sizeof(*hso_net));
+
+ /* most of the setup is done by standard function */
+ ether_setup(net);
+
+ /* fill in the other fields */
+ net->open = hso_net_open;
+ net->stop = hso_net_close;
+ net->hard_start_xmit = hso_net_start_xmit;
+ net->do_ioctl = hso_net_ioctl;
+ net->get_stats = hso_net_get_stats;
+ net->tx_timeout = hso_net_tx_timeout;
+ net->watchdog_timeo = HSO_NET_TX_TIMEOUT;
+ net->set_multicast_list = hso_net_set_multicast;
+ net->flags |= IFF_NOARP;
+ net->mtu = DEFAULT_MTU - 14;
+ net->tx_queue_len = 10;
+ hso_net->skb_rx_buf = NULL;
+ hso_net->rx_parse_state = WAIT_IP;
+ hso_net->wMaxSegmentSize = DEFAULT_MTU;
+ /* always use all multi, no lists of multicasts */
+ hso_net->wNumberMCFilters = 0;
+
+ /* and initialize the semaphore */
+ spin_lock_init(&hso_net->net_lock);
+}
+
+/* setting the mac-address of the newly created iface */
+static void set_ethernet_addr(struct hso_net *odev)
+{
+ unsigned char mac_addr[6];
+ unsigned char dummy_mac[6];
+ int i = 0;
+ int len = 0;
+ unsigned char buffer[13];
+ unsigned char checkserial[7] = "Serial";
+
+ /* we can't fail, therefor we use a default macaddress (constructed
+ * from the pointer of our struct) */
+ mac_addr[0] = 0x00;
+ mac_addr[1] = 0x03;
+ mac_addr[2] = (unsigned char)(((unsigned long)odev) & 0xFF);
+ mac_addr[3] = (unsigned char)((((unsigned long)odev) >> 8) & 0xFF);
+ mac_addr[4] = (unsigned char)((((unsigned long)odev) >> 16) & 0xFF);
+ mac_addr[5] = (unsigned char)((((unsigned long)odev) >> 24) & 0xFF);
+ dummy_mac[0] = 0xFA;
+ dummy_mac[1] = mac_addr[1];
+ dummy_mac[2] = mac_addr[2];
+ dummy_mac[3] = mac_addr[3];
+ dummy_mac[4] = mac_addr[4];
+ dummy_mac[5] = mac_addr[5];
+
+ /* but we like consistency, that's why we try to use the serial number
+ * as a macaddress if available */
+ len = usb_string(odev->parent->usb,
+ odev->parent->usb->descriptor.iSerialNumber,
+ buffer, 13);
+ if (len != 12) {
+ /* some devices don't have the serial filled in, checking ... */
+ for (i = 0; i < 6; i++) {
+ if (buffer[i] != checkserial[i]) {
+ /* Fill in the mac_addr */
+ for (i = 2; i < 6; i++) {
+ if ((16 == buffer[2 * i]) ||
+ (16 == buffer[2 * i + 1])) {
+ dev_err(&odev->parent->usb->dev,
+ "Bad value in MAC "
+ "address i:%d\n", i);
+ } else {
+ mac_addr[i] =
+ (hex2dec(buffer[2 * i]) <<
+ 4) + hex2dec(buffer[2 * i +
+ 1]);
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ dev_err(&odev->parent->usb->dev, "Attempting to get MAC "
+ "address failed: using default\n");
+ }
+
+ /* Now copy it over to our network device structure */
+ memcpy(odev->net->dev_addr, mac_addr, sizeof(mac_addr));
+
+ /* Create the default fake ethernet header. */
+ memcpy(odev->dummy_eth_head.h_dest, mac_addr, ETH_ALEN);
+ memcpy(odev->dummy_eth_head.h_source, dummy_mac, ETH_ALEN);
+ odev->dummy_eth_head.h_proto = htons(ETH_P_IP);
+}
+
+/* Adds a network device in the network device table */
+static int add_net_device(struct hso_device *hso_dev)
+{
+ int i;
+
+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
+ if (network_table[i] == NULL) {
+ network_table[i] = hso_dev;
+ break;
+ }
+ }
+ if (i == HSO_MAX_NET_DEVICES)
+ return -1;
+ return 0;
+}
+
+/* Removes a network device in the network device table */
+static int remove_net_device(struct hso_device *hso_dev)
+{
+ int i;
+
+ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
+ if (network_table[i] == hso_dev) {
+ network_table[i] = NULL;
+ break;
+ }
+ }
+ if (i == HSO_MAX_NET_DEVICES)
+ return -1;
+ return 0;
+}
+
+static struct hso_serial *get_serial_by_shared_int_and_type(struct
+ hso_shared_int
+ *shared_int,
+ int mux)
+{
+ int i, port;
+
+ port = hso_mux_to_port(mux);
+
+ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
+ if (serial_table[i]
+ && (dev2ser(serial_table[i])->shared_int == shared_int)
+ && ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
+ return dev2ser(serial_table[i]);
+ }
+ }
+
+ return NULL;
+}
+
+static struct hso_serial *get_serial_by_index(unsigned index)
+{
+ struct hso_serial *serial = NULL;
+ unsigned long flags;
+
+ if (!serial_table[index])
+ return NULL;
+ spin_lock_irqsave(&serial_table_lock, flags);
+ serial = dev2ser(serial_table[index]);
+ spin_unlock_irqrestore(&serial_table_lock, flags);
+
+ return serial;
+}
+
+static int get_free_serial_index(void)
+{
+ int index = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serial_table_lock, flags);
+ for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) {
+ if (serial_table[index] == NULL) {
+ spin_unlock_irqrestore(&serial_table_lock, flags);
+ return index;
+ }
+ }
+ spin_unlock_irqrestore(&serial_table_lock, flags);
+
+ printk(KERN_ERR "%s: no free serial devices in table\n", __func__);
+ return -1;
+}
+
+static void set_serial_by_index(unsigned index, struct hso_serial *serial)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&serial_table_lock, flags);
+ if (serial)
+ serial_table[index] = serial->parent;
+ else
+ serial_table[index] = NULL;
+ spin_unlock_irqrestore(&serial_table_lock, flags);
+}
+
+/* log a meaningfull explanation of an USB status to syslog */
+static void log_usb_status(int status, const char *function)
+{
+ char *explanation = NULL;
+
+ switch (status) {
+ case -ENODEV:
+ explanation = "no device";
+ break;
+ case -ENOENT:
+ explanation = "endpoint not enabled";
+ break;
+ case -EPIPE:
+ explanation = "endpoint stalled";
+ break;
+ case -ENOSPC:
+ explanation = "not enough bandwidth";
+ break;
+ case -ESHUTDOWN:
+ explanation = "device disabled";
+ break;
+ case -EHOSTUNREACH:
+ explanation = "device suspended";
+ break;
+ case -EINVAL:
+ case -EAGAIN:
+ case -EFBIG:
+ case -EMSGSIZE:
+ explanation = "internal error";
+ break;
+ default:
+ explanation = "unknown status";
+ break;
+ }
+ D1("%s: received USB status - %s (%d)", function, explanation, status);
+}
+
+/* Module definitions */
+module_init(hso_init);
+module_exit(hso_exit);
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESCRIPTION);
+MODULE_LICENSE(MOD_LICENSE);
+MODULE_INFO(Version, DRIVER_VERSION);
+
+/* Module parameter to allow procfs (eg: insmod hso.ko procfs=1) */
+MODULE_PARM_DESC(procfs, "Allow procfs [0 | 1]");
+module_param(procfs, int, S_IRUGO | S_IWUSR);
+
+/* change the debug level (eg: insmod hso.ko debug=0x04) */
+MODULE_PARM_DESC(debug, "Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10]");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+/* set the major tty number (eg: insmod hso.ko tty_major=245) */
+MODULE_PARM_DESC(tty_major, "Set the major tty number");
+module_param(tty_major, int, S_IRUGO | S_IWUSR);
+
+/* set the major tty number (eg: insmod hso.ko tty_major=245) */
+MODULE_PARM_DESC(disable_net, "Disable the network interface");
+module_param(disable_net, int, S_IRUGO | S_IWUSR);
+
+/* enable autosuspend (default disabled) (eg: insmod hso.ko enable_autopm=1) */
+MODULE_PARM_DESC(enable_autopm,
+ "Enable the auto power managment (autosuspend)");
+module_param(enable_autopm, int, S_IRUGO | S_IWUSR);
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 54+ messages in thread[parent not found: <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-14 21:59 ` Matthew Dharm 2008-04-14 22:42 ` Andrew Bird (Sphere Systems) 2008-04-14 23:20 ` Paulius Zaleckas ` (5 subsequent siblings) 6 siblings, 1 reply; 54+ messages in thread From: Matthew Dharm @ 2008-04-14 21:59 UTC (permalink / raw) To: Greg KH Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io [-- Attachment #1: Type: text/plain, Size: 921 bytes --] I will be the first to admit that I'm picking a nit here, but... On Mon, Apr 14, 2008 at 02:32:39PM -0700, Greg KH wrote: > Subject: USB: add option hso driver > +config USB_HSO > + tristate "Option USB High Speed Mobile Devices" > + * Driver for Option High Speed Mobile Devices. > +#define MOD_DESCRIPTION "USB High Speed Option driver" Would it be possible to pick one of "High Speed Option" and "Option... High Speed" and then be consistent with the acronym of "hso" or "ohs"? Or maybe call it "option_hs" or something even more clear? Mixing them just makes it more difficult for people to use grep and google. Matt -- Matthew Dharm Home: mdharm-usb@one-eyed-alien.net Maintainer, Linux USB Mass Storage Driver DP: And judging from the scores, Stef has the sma... T: LET'S NOT GO THERE! -- Dust Puppy and Tanya User Friendly, 12/11/1997 [-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-14 21:59 ` Matthew Dharm @ 2008-04-14 22:42 ` Andrew Bird (Sphere Systems) 2008-04-14 23:03 ` Greg KH 0 siblings, 1 reply; 54+ messages in thread From: Andrew Bird (Sphere Systems) @ 2008-04-14 22:42 UTC (permalink / raw) To: Matthew Dharm Cc: Greg KH, linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas Hi Matthew, I'd prefer the keep it as just 'hso' and change the textual description if necessary. I think module names are just arbitrary, and should not necessarily reflect the original usage too well, otherwise you end up with a situation like with the 'option' module. That module is now used for many devices from differing manufacturers and it doesn't really make much sense to load the option module to service a device from Novatel, Sierra Wireless or Huawei. Just my two pence, Andrew On Monday 14 April 2008 22:59, Matthew Dharm wrote: > I will be the first to admit that I'm picking a nit here, but... > > On Mon, Apr 14, 2008 at 02:32:39PM -0700, Greg KH wrote: > > Subject: USB: add option hso driver > > > > +config USB_HSO > > + tristate "Option USB High Speed Mobile Devices" > > > > + * Driver for Option High Speed Mobile Devices. > > > > +#define MOD_DESCRIPTION "USB High Speed Option driver" > > Would it be possible to pick one of "High Speed Option" and "Option... > High Speed" and then be consistent with the acronym of "hso" or "ohs"? Or > maybe call it "option_hs" or something even more clear? > > Mixing them just makes it more difficult for people to use grep and google. > > Matt ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-14 22:42 ` Andrew Bird (Sphere Systems) @ 2008-04-14 23:03 ` Greg KH [not found] ` <20080414230309.GA1672-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Greg KH @ 2008-04-14 23:03 UTC (permalink / raw) To: Andrew Bird (Sphere Systems) Cc: Matthew Dharm, linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas On Mon, Apr 14, 2008 at 11:42:07PM +0100, Andrew Bird (Sphere Systems) wrote: > Hi Matthew, > I'd prefer the keep it as just 'hso' and change the textual description if > necessary. I think module names are just arbitrary, and should not > necessarily reflect the original usage too well, otherwise you end up with a > situation like with the 'option' module. That module is now used for many > devices from differing manufacturers and it doesn't really make much sense to > load the option module to service a device from Novatel, Sierra Wireless or > Huawei. Ok, that's fine with me, what should we use for the text in the driver and the Kconfig entry? thanks, greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <20080414230309.GA1672-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414230309.GA1672-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-15 8:01 ` Filip Aben 2008-04-15 15:40 ` Greg KH 0 siblings, 1 reply; 54+ messages in thread From: Filip Aben @ 2008-04-15 8:01 UTC (permalink / raw) To: Greg KH Cc: Andrew Bird (Sphere Systems), Matthew Dharm, linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas " This driver is used for Option HSDPA/HSUPA cards that support downlink speeds of 7.2Mbps or greater. " How does that sound ? Regards, Filip- On Mon, 14 Apr 2008, Greg KH wrote: > On Mon, Apr 14, 2008 at 11:42:07PM +0100, Andrew Bird (Sphere Systems) wrote: >> Hi Matthew, >> I'd prefer the keep it as just 'hso' and change the textual description if >> necessary. I think module names are just arbitrary, and should not >> necessarily reflect the original usage too well, otherwise you end up with a >> situation like with the 'option' module. That module is now used for many >> devices from differing manufacturers and it doesn't really make much sense to >> load the option module to service a device from Novatel, Sierra Wireless or >> Huawei. > > Ok, that's fine with me, what should we use for the text in the driver > and the Kconfig entry? > > thanks, > > greg k-h > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 8:01 ` Filip Aben @ 2008-04-15 15:40 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 15:40 UTC (permalink / raw) To: Filip Aben Cc: Andrew Bird (Sphere Systems), Matthew Dharm, linux-usb, netdev, Alan Cox, Paulius Zaleckas On Tue, Apr 15, 2008 at 10:01:14AM +0200, Filip Aben wrote: > > " > This driver is used for Option HSDPA/HSUPA cards that support > downlink speeds of 7.2Mbps or greater. > " > > How does that sound ? Sounds good, I've updated the Kconfig entry with it. thanks, greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 2008-04-14 21:59 ` Matthew Dharm @ 2008-04-14 23:20 ` Paulius Zaleckas 2008-04-15 8:10 ` Oliver Neukum 2008-04-15 11:44 ` Oliver Neukum ` (4 subsequent siblings) 6 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-14 23:20 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 307 bytes --] Greg KH wrote: > I'd like to see this go into 2.6.26, so any review comments by anyone > who wishes to review any portion of this would be greatly apprecited. In attached patch I have rewrote MAC address generation to simple call to random_ether_addr instead of some mysterious pointer parsing :) Paulius [-- Attachment #2: hso_random.patch --] [-- Type: text/x-patch, Size: 2605 bytes --] diff --git a/ldp/usb-add-option-hso-driver.patch b/ldp/usb-add-option-hso-driver.patch index 429f9c9..7360e04 100644 --- a/ldp/usb-add-option-hso-driver.patch +++ b/ldp/usb-add-option-hso-driver.patch @@ -56,7 +56,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> obj-$(CONFIG_USB_NET_DM9601) += dm9601.o --- /dev/null +++ b/drivers/net/usb/hso.c -@@ -0,0 +1,3349 @@ +@@ -0,0 +1,3343 @@ +/****************************************************************************** + * + * Driver for Option High Speed Mobile Devices. @@ -1523,7 +1523,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> + return 0; +} + -+/* Toggles radioon or off ( used by ioctl ) */ ++/* Toggles radio on or off ( used by ioctl ) */ +static int hso_set_radio(struct hso_device *hso_dev, int enabled) +{ + if (!hso_dev) @@ -2350,7 +2350,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> + spin_lock_init(&serial->serial_lock); + serial->num_rx_urbs = num_urbs; + -+ /* RX , allocate urb and initialize */ ++ /* RX, allocate urb and initialize */ + + /* prepare our RX buffer */ + serial->rx_data_length = rx_size; @@ -2370,7 +2370,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> + } + } + -+ /* TX , allocate urb and initialize */ ++ /* TX, allocate urb and initialize */ + serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!serial->tx_urb) { + dev_err(dev, "Could not allocate urb?\n"); @@ -3190,14 +3190,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> + unsigned char buffer[13]; + unsigned char checkserial[7] = "Serial"; + -+ /* we can't fail, therefor we use a default macaddress (constructed -+ * from the pointer of our struct) */ -+ mac_addr[0] = 0x00; -+ mac_addr[1] = 0x03; -+ mac_addr[2] = (unsigned char)(((unsigned long)odev) & 0xFF); -+ mac_addr[3] = (unsigned char)((((unsigned long)odev) >> 8) & 0xFF); -+ mac_addr[4] = (unsigned char)((((unsigned long)odev) >> 16) & 0xFF); -+ mac_addr[5] = (unsigned char)((((unsigned long)odev) >> 24) & 0xFF); ++ /* we can't fail, therefor we use a random macaddress */ ++ random_ether_addr(mac_addr); + dummy_mac[0] = 0xFA; + dummy_mac[1] = mac_addr[1]; + dummy_mac[2] = mac_addr[2]; @@ -3233,7 +3227,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> + } + } else { + dev_err(&odev->parent->usb->dev, "Attempting to get MAC " -+ "address failed: using default\n"); ++ "address failed: using random\n"); + } + + /* Now copy it over to our network device structure */ ^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-14 23:20 ` Paulius Zaleckas @ 2008-04-15 8:10 ` Oliver Neukum [not found] ` <200804151010.33688.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 8:10 UTC (permalink / raw) To: Paulius Zaleckas Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA Am Dienstag, 15. April 2008 01:20:51 schrieb Paulius Zaleckas: > Greg KH wrote: > > I'd like to see this go into 2.6.26, so any review comments by anyone > > who wishes to review any portion of this would be greatly apprecited. > > In attached patch I have rewrote MAC address generation to simple call > to random_ether_addr instead of some mysterious pointer parsing :) Could you please generate a kernel patch, not a patch against a patch? I have difficulties dealing with that. Regards Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804151010.33688.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804151010.33688.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 8:58 ` Paulius Zaleckas [not found] ` <48046E4A.3060901-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-15 8:58 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 164 bytes --] Oliver Neukum wrote: > Could you please generate a kernel patch, not a patch against a patch? > I have difficulties dealing with that. OK. Here it goes. Paulius [-- Attachment #2: kernel_hso_random.patch --] [-- Type: text/x-patch, Size: 2002 bytes --] --- linux-2.6-hso/drivers/net/usb/hso.c 2008-04-15 11:26:05.000000000 +0300 +++ linux-2.6-hso-my/drivers/net/usb/hso.c 2008-04-15 11:52:25.000000000 +0300 @@ -1464,7 +1464,7 @@ static int hso_set_suspend(struct hso_de return 0; } -/* Toggles radioon or off ( used by ioctl ) */ +/* Toggles radio on or off ( used by ioctl ) */ static int hso_set_radio(struct hso_device *hso_dev, int enabled) { if (!hso_dev) @@ -2291,7 +2291,7 @@ static int hso_serial_common_create(stru spin_lock_init(&serial->serial_lock); serial->num_rx_urbs = num_urbs; - /* RX , allocate urb and initialize */ + /* RX, allocate urb and initialize */ /* prepare our RX buffer */ serial->rx_data_length = rx_size; @@ -2311,7 +2311,7 @@ static int hso_serial_common_create(stru } } - /* TX , allocate urb and initialize */ + /* TX, allocate urb and initialize */ serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!serial->tx_urb) { dev_err(dev, "Could not allocate urb?\n"); @@ -3131,14 +3131,8 @@ static void set_ethernet_addr(struct hso unsigned char buffer[13]; unsigned char checkserial[7] = "Serial"; - /* we can't fail, therefor we use a default macaddress (constructed - * from the pointer of our struct) */ - mac_addr[0] = 0x00; - mac_addr[1] = 0x03; - mac_addr[2] = (unsigned char)(((unsigned long)odev) & 0xFF); - mac_addr[3] = (unsigned char)((((unsigned long)odev) >> 8) & 0xFF); - mac_addr[4] = (unsigned char)((((unsigned long)odev) >> 16) & 0xFF); - mac_addr[5] = (unsigned char)((((unsigned long)odev) >> 24) & 0xFF); + /* we can't fail, therefor we use a random macaddress */ + random_ether_addr(mac_addr); dummy_mac[0] = 0xFA; dummy_mac[1] = mac_addr[1]; dummy_mac[2] = mac_addr[2]; @@ -3174,7 +3168,7 @@ static void set_ethernet_addr(struct hso } } else { dev_err(&odev->parent->usb->dev, "Attempting to get MAC " - "address failed: using default\n"); + "address failed: using random\n"); } /* Now copy it over to our network device structure */ ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <48046E4A.3060901-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <48046E4A.3060901-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-15 15:39 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 15:39 UTC (permalink / raw) To: Paulius Zaleckas Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA On Tue, Apr 15, 2008 at 11:58:50AM +0300, Paulius Zaleckas wrote: > Oliver Neukum wrote: >> Could you please generate a kernel patch, not a patch against a patch? >> I have difficulties dealing with that. > > OK. Here it goes. Thanks, I've merged these changes in. greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 2008-04-14 21:59 ` Matthew Dharm 2008-04-14 23:20 ` Paulius Zaleckas @ 2008-04-15 11:44 ` Oliver Neukum [not found] ` <200804151344.42085.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-15 13:06 ` Oliver Neukum ` (3 subsequent siblings) 6 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 11:44 UTC (permalink / raw) To: Greg KH Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > Hi all, > > Here's a patch that I have cleaned up for context only from Option that > is a USB serial / network device all in one. > > I'd like to see this go into 2.6.26, so any review comments by anyone > who wishes to review any portion of this would be greatly apprecited. Hi, this patch against Greg's version with Pauliaus patch applied - uses correct CDC includes and constants - fixes a race between disconnect and open - fixes a race between probe and open - corrects incorrect uses of GFP_KERNEL - adds some error handling in open - fixes races in access to urb->status There's still a race condition in the write path left and the autosuspend handling is broken in extremely interesting ways. The next patch will fix these and I am still doing further reviews. Regards Oliver Signed-off-by: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org> --- --- linux-2.6.25-rc7-vanilla/drivers/net/usb/hso.c 2008-04-15 13:33:25.000000000 +0200 +++ linux-2.6.25-rc7-work/drivers/net/usb/hso.c 2008-04-15 13:07:21.000000000 +0200 @@ -60,6 +60,7 @@ #include <linux/ip.h> #include <linux/proc_fs.h> #include <linux/uaccess.h> +#include <linux/usb/cdc.h> #include <net/arp.h> #include <asm/byteorder.h> @@ -94,25 +95,10 @@ #define HSO_NET_TX_TIMEOUT (HZ*10) -#define SEND_ENCAPSULATED_COMMAND 0x00 -#define GET_ENCAPSULATED_RESPONSE 0x01 - /* Serial port defines and structs. */ #define HSO_THRESHOLD_THROTTLE (7*1024) #define HSO_THRESHOLD_UNTHROTTLE (2*1024) -/* These definitions used in the Ethernet Packet Filtering requests */ -/* See CDC Spec Table 62 */ -#define MODE_FLAG_PROMISCUOUS (1<<0) -#define MODE_FLAG_ALL_MULTICAST (1<<1) -#define MODE_FLAG_DIRECTED (1<<2) -#define MODE_FLAG_BROADCAST (1<<3) -#define MODE_FLAG_MULTICAST (1<<4) - -/* CDC Spec class requests - CDC Spec Table 46 */ -#define SET_ETHERNET_MULTICAST_FILTER 0x40 -#define SET_ETHERNET_PACKET_FILTER 0x43 - #define HSO_SERIAL_FLAG_THROTTLED 0 #define HSO_SERIAL_FLAG_TX_SENT 1 #define HSO_SERIAL_FLAG_RX_SENT 2 @@ -262,6 +248,7 @@ struct hso_device { struct device *dev; struct kref ref; + struct mutex mutex; /* TODO: Not sure about the ones below */ struct proc_dir_entry *ourproc; @@ -339,7 +326,7 @@ static struct usb_endpoint_descriptor *h 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); +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); @@ -655,12 +642,12 @@ static int hso_net_open(struct net_devic hso_start_net_device(odev->parent); - /* Tell the kernel we are ready to start receiving from it */ - netif_start_queue(net); - /* We are up and running. */ set_bit(HSO_NET_RUNNING, &odev->flags); + /* Tell the kernel we are ready to start receiving from it */ + netif_start_queue(net); + return 0; } @@ -669,10 +656,10 @@ static int hso_net_close(struct net_devi { struct hso_net *odev = netdev_priv(net); - /* no longer running */ - clear_bit(HSO_NET_RUNNING, &odev->flags); /* we don't need the queue anymore */ netif_stop_queue(net); + /* no longer running */ + clear_bit(HSO_NET_RUNNING, &odev->flags); hso_stop_net_device(odev->parent); @@ -834,34 +821,34 @@ static void hso_net_set_multicast(struct if (net->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ D1("%s: Promiscuous mode enabled", net->name); - odev->mode_flags = MODE_FLAG_ALL_MULTICAST | - MODE_FLAG_DIRECTED | - MODE_FLAG_BROADCAST | - MODE_FLAG_MULTICAST | - MODE_FLAG_PROMISCUOUS; + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST | + USB_CDC_PACKET_TYPE_PROMISCUOUS; } else if (net->mc_count > odev->wNumberMCFilters) { /* Too many to filter perfectly -- accept all multicasts. */ D1("%s: too many MC filters for hardware, using allmulti", net->name); - odev->mode_flags = MODE_FLAG_ALL_MULTICAST | - MODE_FLAG_DIRECTED | - MODE_FLAG_BROADCAST | - MODE_FLAG_MULTICAST; + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; } else if (net->flags & IFF_ALLMULTI) { /* Filter in software */ D1("%s: using allmulti", net->name); - odev->mode_flags = MODE_FLAG_ALL_MULTICAST | - MODE_FLAG_DIRECTED | - MODE_FLAG_BROADCAST | - MODE_FLAG_MULTICAST; + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; } else { /* do multicast filtering in hardware */ struct dev_mc_list *mclist; D1("%s: set multicast filters", net->name); - odev->mode_flags = MODE_FLAG_ALL_MULTICAST | - MODE_FLAG_DIRECTED | - MODE_FLAG_BROADCAST | - MODE_FLAG_MULTICAST; + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; buf = kmalloc(6 * net->mc_count, GFP_ATOMIC); if (!buf) { dev_err(&net->dev, "No memory to allocate?"); @@ -885,7 +872,6 @@ static void read_bulk_callback(struct ur struct hso_net *odev = urb->context; struct net_device *net = NULL; int result = 0; - unsigned long flags = 0; int status = urb->status; /* is al ok? (Filip: Who's Al ?) */ @@ -923,11 +909,11 @@ static void read_bulk_callback(struct ur if (urb->actual_length) { /* Handle the IP stream, add header and push it onto network * stack if the packet is complete. */ - spin_lock_irqsave(&odev->net_lock, flags); + spin_lock(&odev->net_lock); packetizeRx(odev, urb->transfer_buffer, urb->actual_length, (urb->transfer_buffer_length > urb->actual_length) ? 1 : 0); - spin_unlock_irqrestore(&odev->net_lock, flags); + spin_unlock(&odev->net_lock); } /* We are done with this URB, resubmit it. Prep the USB to wait for @@ -1198,8 +1184,7 @@ static void _hso_serial_set_termios(stru static int hso_serial_open(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = get_serial_by_index(tty->index); - int result = 0; - unsigned long flags; + int result; /* sanity check */ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { @@ -1208,12 +1193,12 @@ static int hso_serial_open(struct tty_st return -ENODEV; } - usb_autopm_get_interface(serial->parent->interface); + mutex_lock(&serial->parent->mutex); + result = usb_autopm_get_interface(serial->parent->interface); + if (result < 0) + goto err_out; D1("Opening %d", serial->minor); - /* lock it down */ - spin_lock_irqsave(&serial->serial_lock, flags); - kref_get(&serial->parent->ref); /* setup */ @@ -1227,7 +1212,7 @@ static int hso_serial_open(struct tty_st serial->flags = 0; /* Force default termio settings */ _hso_serial_set_termios(tty, NULL); - result = hso_start_serial_device(serial->parent); + result = hso_start_serial_device(serial->parent, GFP_KERNEL); if (result) { hso_stop_serial_device(serial->parent); serial->open_count--; @@ -1237,13 +1222,13 @@ static int hso_serial_open(struct tty_st D1("Port was already open"); } - spin_unlock_irqrestore(&serial->serial_lock, flags); - usb_autopm_put_interface(serial->parent->interface); /* done */ if (result) hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); +err_out: + mutex_unlock(&serial->parent->mutex); return result; } @@ -1251,7 +1236,6 @@ static int hso_serial_open(struct tty_st static void hso_serial_close(struct tty_struct *tty, struct file *filp) { struct hso_serial *serial = tty->driver_data; - unsigned long flags; u8 usb_gone; D1("Closing serial port"); @@ -1262,6 +1246,7 @@ static void hso_serial_close(struct tty_ return; } + mutex_lock(&serial->parent->mutex); usb_gone = test_bit(HSO_SERIAL_USB_GONE, &serial->flags); if (!usb_gone) @@ -1269,7 +1254,6 @@ static void hso_serial_close(struct tty_ /* reset the rts and dtr */ /* do the actual close */ - spin_lock_irqsave(&serial->serial_lock, flags); serial->open_count--; if (serial->open_count <= 0) { kref_put(&serial->parent->ref, hso_serial_ref_free); @@ -1281,11 +1265,9 @@ static void hso_serial_close(struct tty_ if (!usb_gone) hso_stop_serial_device(serial->parent); } - if (serial->open_count < 0) - serial->open_count = 0; - spin_unlock_irqrestore(&serial->serial_lock, flags); if (!usb_gone) usb_autopm_put_interface(serial->parent->interface); + mutex_unlock(&serial->parent->mutex); } /* close the requested serial port */ @@ -1639,24 +1621,24 @@ static int mux_device_request(struct hso ctrl_req->wIndex = hso_port_to_mux(port); ctrl_req->wLength = size; - if (type == GET_ENCAPSULATED_RESPONSE) { + if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) { /* Reading command */ ctrl_req->bRequestType = USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE; - ctrl_req->bRequest = GET_ENCAPSULATED_RESPONSE; + ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; pipe = usb_rcvctrlpipe(serial->parent->usb, 0); } else { /* Writing command */ ctrl_req->bRequestType = USB_DIR_OUT | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE; - ctrl_req->bRequest = SEND_ENCAPSULATED_COMMAND; + ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; pipe = usb_sndctrlpipe(serial->parent->usb, 0); } /* syslog */ D2("%s command (%02x) len: %d, port: %d", - type == GET_ENCAPSULATED_RESPONSE ? "Read" : "Write", + type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write", ctrl_req->bRequestType, ctrl_req->wLength, port); /* Load ctrl urb */ @@ -1696,7 +1678,7 @@ static int hso_mux_serial_read(struct hs return 0; } return mux_device_request(serial, - GET_ENCAPSULATED_RESPONSE, + USB_CDC_GET_ENCAPSULATED_RESPONSE, serial->parent->port_spec & HSO_PORT_MASK, serial->rx_urb[0], &serial->ctrl_req_rx, @@ -1759,7 +1741,7 @@ static int hso_mux_serial_write_data(str return -EINVAL; return mux_device_request(serial, - SEND_ENCAPSULATED_COMMAND, + USB_CDC_SEND_ENCAPSULATED_COMMAND, serial->parent->port_spec & HSO_PORT_MASK, serial->tx_urb, &serial->ctrl_req_tx, @@ -2145,20 +2127,17 @@ static int hso_stop_net_device(struct hs return -ENODEV; for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { - if (hso_net->mux_bulk_rx_urb_pool[i] - && (hso_net->mux_bulk_rx_urb_pool[i]->status == - -EINPROGRESS)) - usb_unlink_urb(hso_net->mux_bulk_rx_urb_pool[i]); + if (hso_net->mux_bulk_rx_urb_pool[i]) + usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]); } - if (hso_net->mux_bulk_tx_urb - && (hso_net->mux_bulk_tx_urb->status == -EINPROGRESS)) - usb_unlink_urb(hso_net->mux_bulk_tx_urb); + if (hso_net->mux_bulk_tx_urb) + usb_kill_urb(hso_net->mux_bulk_tx_urb); return 0; } -static int hso_start_serial_device(struct hso_device *hso_dev) +static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) { int i, result = 0; struct hso_serial *serial = dev2ser(hso_dev); @@ -2180,7 +2159,7 @@ static int hso_start_serial_device(struc serial->rx_data_length, hso_std_serial_read_bulk_callback, serial); - result = usb_submit_urb(serial->rx_urb[i], GFP_KERNEL); + result = usb_submit_urb(serial->rx_urb[i], flags); if (result) { dev_warn(&serial->parent->usb->dev, "Failed to submit urb - res %d\n", @@ -2361,6 +2340,7 @@ static struct hso_device *hso_create_dev hso_dev->usb = interface_to_usbdev(intf); hso_dev->interface = intf; kref_init(&hso_dev->ref); + mutex_init(&hso_dev->mutex); INIT_WORK(&hso_dev->async_get_intf, async_get_intf); INIT_WORK(&hso_dev->async_put_intf, async_put_intf); @@ -2959,7 +2939,7 @@ static int hso_resume(struct usb_interfa if (serial_table[i] && (serial_table[i]->interface == iface)) { if (dev2ser(serial_table[i])->open_count) { result = - hso_start_serial_device(serial_table[i]); + hso_start_serial_device(serial_table[i], GFP_NOIO); hso_kick_transmit(dev2ser(serial_table[i])); if (result) goto out; @@ -2999,15 +2979,18 @@ static void hso_serial_ref_free(struct k static void hso_free_interface(struct usb_interface *interface) { + struct hso_serial *hso_dev; int i; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == interface)) { - if (dev2ser(serial_table[i])->tty) - tty_hangup(dev2ser(serial_table[i])->tty); - set_bit(HSO_SERIAL_USB_GONE, - &dev2ser(serial_table[i])->flags); + hso_dev = dev2ser(serial_table[i]); + if (hso_dev->tty) + tty_hangup(hso_dev->tty); + mutex_lock(&hso_dev->parent->mutex); + set_bit(HSO_SERIAL_USB_GONE, &hso_dev->flags); + mutex_unlock(&hso_dev->parent->mutex); kref_put(&serial_table[i]->ref, hso_serial_ref_free); } } @@ -3069,7 +3052,7 @@ static int hso_get_mux_ports(struct usb_ static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int, struct usb_device *usb, gfp_t gfp) { - int result = 0; + int result; usb_fill_int_urb(shared_int->shared_intr_urb, usb, usb_rcvintpipe(usb, -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804151344.42085.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804151344.42085.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 16:06 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 16:06 UTC (permalink / raw) To: Oliver Neukum Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 01:44:40PM +0200, Oliver Neukum wrote: > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > Hi all, > > > > Here's a patch that I have cleaned up for context only from Option that > > is a USB serial / network device all in one. > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > who wishes to review any portion of this would be greatly apprecited. > > Hi, > > this patch against Greg's version with Pauliaus patch applied > > - uses correct CDC includes and constants > - fixes a race between disconnect and open > - fixes a race between probe and open > - corrects incorrect uses of GFP_KERNEL > - adds some error handling in open > - fixes races in access to urb->status > > There's still a race condition in the write path left and the autosuspend > handling is broken in extremely interesting ways. The next patch will fix > these and I am still doing further reviews. Thanks a lot for these changes, I've applied them to the driver. greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> ` (2 preceding siblings ...) 2008-04-15 11:44 ` Oliver Neukum @ 2008-04-15 13:06 ` Oliver Neukum [not found] ` <200804151506.21856.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-15 16:08 ` Greg KH 2008-04-15 13:25 ` Oliver Neukum ` (2 subsequent siblings) 6 siblings, 2 replies; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 13:06 UTC (permalink / raw) To: Greg KH Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > Hi all, > > Here's a patch that I have cleaned up for context only from Option that > is a USB serial / network device all in one. > > I'd like to see this go into 2.6.26, so any review comments by anyone > who wishes to review any portion of this would be greatly apprecited. This incremental patch fixes a race where write can mess with an URB while it isn't yet returned. Regards Oliver Signed-off-by: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org> --- --- linux-2.6.25-rc7-work/drivers/net/usb/hso.c.alt 2008-04-15 13:59:21.000000000 +0200 +++ linux-2.6.25-rc7-work/drivers/net/usb/hso.c 2008-04-15 14:44:45.000000000 +0200 @@ -220,6 +220,7 @@ struct hso_serial { unsigned long flags; u8 rts_state; u8 dtr_state; + int tx_urb_used:1; /* from usb_serial_port */ struct tty_struct *tty; @@ -1521,12 +1522,13 @@ static void hso_kick_transmit(struct hso { u8 *temp = NULL; unsigned long flags; + int res; spin_lock_irqsave(&serial->serial_lock, flags); if (!serial->tx_buffer_count) goto out; - if (serial->tx_urb->status == -EINPROGRESS) + if (serial->tx_urb_used) goto out; /* Wakeup USB interface if necessary */ @@ -1540,10 +1542,13 @@ static void hso_kick_transmit(struct hso serial->tx_data_count = serial->tx_buffer_count; serial->tx_buffer_count = 0; -out: /* If temp is set, it means we switched buffers */ - if (temp && serial->write_data) - serial->write_data(serial); + if (temp && serial->write_data) { + res = serial->write_data(serial); + if (res >= 0) + serial->tx_urb_used = 1; + } +out: spin_unlock_irqrestore(&serial->serial_lock, flags); } @@ -1783,6 +1788,9 @@ static void ctrl_callback(struct urb *ur if (!serial) return; + spin_lock(&serial->serial_lock); + serial->tx_urb_used = 0; + spin_unlock(&serial->serial_lock); if (status) { log_usb_status(status, __func__); return; @@ -1914,6 +1922,9 @@ static void hso_std_serial_write_bulk_ca return; } + spin_lock(&serial->serial_lock); + serial->tx_urb_used = 0; + spin_unlock(&serial->serial_lock); if (status) { log_usb_status(status, __func__); return; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804151506.21856.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804151506.21856.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 16:08 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 16:08 UTC (permalink / raw) To: Oliver Neukum Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 03:06:19PM +0200, Oliver Neukum wrote: > --- linux-2.6.25-rc7-work/drivers/net/usb/hso.c.alt 2008-04-15 13:59:21.000000000 +0200 > +++ linux-2.6.25-rc7-work/drivers/net/usb/hso.c 2008-04-15 14:44:45.000000000 +0200 > @@ -220,6 +220,7 @@ struct hso_serial { > unsigned long flags; > u8 rts_state; > u8 dtr_state; > + int tx_urb_used:1; Bitfields should be "unsigned", I've changed this so sparse does not complain. thanks, greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 13:06 ` Oliver Neukum [not found] ` <200804151506.21856.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 16:08 ` Greg KH 1 sibling, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 16:08 UTC (permalink / raw) To: Oliver Neukum Cc: linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas, ajb On Tue, Apr 15, 2008 at 03:06:19PM +0200, Oliver Neukum wrote: > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > Hi all, > > > > Here's a patch that I have cleaned up for context only from Option that > > is a USB serial / network device all in one. > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > who wishes to review any portion of this would be greatly apprecited. > > This incremental patch fixes a race where write can mess with > an URB while it isn't yet returned. > > Regards > Oliver > > Signed-off-by: Oliver Neukum <oneukum@suse.de> Applied to the driver, thanks. greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> ` (3 preceding siblings ...) 2008-04-15 13:06 ` Oliver Neukum @ 2008-04-15 13:25 ` Oliver Neukum 2008-04-15 14:12 ` Filip Aben 2008-04-15 22:49 ` Paulius Zaleckas 2008-04-16 13:11 ` Paulius Zaleckas 6 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 13:25 UTC (permalink / raw) To: Greg KH Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > Hi all, > > Here's a patch that I have cleaned up for context only from Option that > is a USB serial / network device all in one. > > I'd like to see this go into 2.6.26, so any review comments by anyone > who wishes to review any portion of this would be greatly apprecited. The driver implements an ioctl to disable autosuspend. This seems unnecessary to me, as you can do that via sysfs, too. Is this included to work around permission problems? Can somebody explain? Regards Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 13:25 ` Oliver Neukum @ 2008-04-15 14:12 ` Filip Aben 2008-04-15 14:14 ` Oliver Neukum 0 siblings, 1 reply; 54+ messages in thread From: Filip Aben @ 2008-04-15 14:12 UTC (permalink / raw) To: Oliver Neukum; +Cc: Greg KH, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb On Tue, 2008-04-15 at 15:25 +0200, Oliver Neukum wrote: > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > Hi all, > > > > Here's a patch that I have cleaned up for context only from Option that > > is a USB serial / network device all in one. > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > who wishes to review any portion of this would be greatly apprecited. > > The driver implements an ioctl to disable autosuspend. This seems > unnecessary to me, as you can do that via sysfs, too. Is this included > to work around permission problems? Can somebody explain? > > Regards > Oliver This is added to support card firmware upgraders who will need to disable suspend, even on (embedded) systems that don't have sysfs mounted. Regards, Filip- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 14:12 ` Filip Aben @ 2008-04-15 14:14 ` Oliver Neukum 2008-04-15 15:03 ` Filip Aben 0 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 14:14 UTC (permalink / raw) To: Filip Aben; +Cc: Greg KH, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb Am Dienstag, 15. April 2008 16:12:22 schrieb Filip Aben: > > On Tue, 2008-04-15 at 15:25 +0200, Oliver Neukum wrote: > > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > > Hi all, > > > > > > Here's a patch that I have cleaned up for context only from Option that > > > is a USB serial / network device all in one. > > > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > > who wishes to review any portion of this would be greatly apprecited. > > > > The driver implements an ioctl to disable autosuspend. This seems > > unnecessary to me, as you can do that via sysfs, too. Is this included > > to work around permission problems? Can somebody explain? > > > > Regards > > Oliver > > This is added to support card firmware upgraders who will need to > disable suspend, even on (embedded) systems that don't have sysfs > mounted. How do they enable autosuspend without sysfs? Regards Oliver ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 14:14 ` Oliver Neukum @ 2008-04-15 15:03 ` Filip Aben 2008-04-15 15:34 ` Greg KH 0 siblings, 1 reply; 54+ messages in thread From: Filip Aben @ 2008-04-15 15:03 UTC (permalink / raw) To: Oliver Neukum; +Cc: Greg KH, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb On Tue, 2008-04-15 at 16:14 +0200, Oliver Neukum wrote: > Am Dienstag, 15. April 2008 16:12:22 schrieb Filip Aben: > > > > On Tue, 2008-04-15 at 15:25 +0200, Oliver Neukum wrote: > > > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > > > Hi all, > > > > > > > > Here's a patch that I have cleaned up for context only from Option that > > > > is a USB serial / network device all in one. > > > > > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > > > who wishes to review any portion of this would be greatly apprecited. > > > > > > The driver implements an ioctl to disable autosuspend. This seems > > > unnecessary to me, as you can do that via sysfs, too. Is this included > > > to work around permission problems? Can somebody explain? > > > > > > Regards > > > Oliver > > > > This is added to support card firmware upgraders who will need to > > disable suspend, even on (embedded) systems that don't have sysfs > > mounted. > > How do they enable autosuspend without sysfs? Good point. No idea. I suppose it's easier to rely on something that will always work, rather then making assumptions about suspend based on whether sysfs is mounted or not. ( Wasn't there a kernel recently that had autosuspend enabled by default ? ) Regards, Filip- ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 15:03 ` Filip Aben @ 2008-04-15 15:34 ` Greg KH [not found] ` <20080415153408.GB7996-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 2008-04-15 18:46 ` Oliver Neukum 0 siblings, 2 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 15:34 UTC (permalink / raw) To: Filip Aben Cc: Oliver Neukum, linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 05:03:09PM +0200, Filip Aben wrote: > > On Tue, 2008-04-15 at 16:14 +0200, Oliver Neukum wrote: > > Am Dienstag, 15. April 2008 16:12:22 schrieb Filip Aben: > > > > > > On Tue, 2008-04-15 at 15:25 +0200, Oliver Neukum wrote: > > > > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > > > > Hi all, > > > > > > > > > > Here's a patch that I have cleaned up for context only from Option that > > > > > is a USB serial / network device all in one. > > > > > > > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > > > > who wishes to review any portion of this would be greatly apprecited. > > > > > > > > The driver implements an ioctl to disable autosuspend. This seems > > > > unnecessary to me, as you can do that via sysfs, too. Is this included > > > > to work around permission problems? Can somebody explain? > > > > > > > > Regards > > > > Oliver > > > > > > This is added to support card firmware upgraders who will need to > > > disable suspend, even on (embedded) systems that don't have sysfs > > > mounted. > > > > How do they enable autosuspend without sysfs? > > Good point. No idea. I suppose it's easier to rely on something that > will always work, rather then making assumptions about suspend based on > whether sysfs is mounted or not. Like I always like to point out, my _phone_ has sysfs mounted, I really doubt any 2.6 embedded system would not have it mounted these days, it is very useful for things like this. So I'll go delete that "special" ioctl, that's not a good thing to do, use the common interfaces that all USB devices rely on instead, don't do something different for just one type of USB device. I'm also a bit concerned about the special "set radio" ioctl as well. Filip, what uses that ioctl? If it's really necessary, can't a sysfs file do the same thing? thanks, greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <20080415153408.GB7996-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080415153408.GB7996-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-15 16:24 ` Filip Aben 2008-04-15 17:58 ` Oliver Neukum 0 siblings, 1 reply; 54+ messages in thread From: Filip Aben @ 2008-04-15 16:24 UTC (permalink / raw) To: Greg KH Cc: Oliver Neukum, linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, 2008-04-15 at 08:34 -0700, Greg KH wrote: > On Tue, Apr 15, 2008 at 05:03:09PM +0200, Filip Aben wrote: > > > > On Tue, 2008-04-15 at 16:14 +0200, Oliver Neukum wrote: > > > Am Dienstag, 15. April 2008 16:12:22 schrieb Filip Aben: > > > > > > > > On Tue, 2008-04-15 at 15:25 +0200, Oliver Neukum wrote: > > > > > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > > > > > Hi all, > > > > > > > > > > > > Here's a patch that I have cleaned up for context only from Option that > > > > > > is a USB serial / network device all in one. > > > > > > > > > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > > > > > who wishes to review any portion of this would be greatly apprecited. > > > > > > > > > > The driver implements an ioctl to disable autosuspend. This seems > > > > > unnecessary to me, as you can do that via sysfs, too. Is this included > > > > > to work around permission problems? Can somebody explain? > > > > > > > > > > Regards > > > > > Oliver > > > > > > > > This is added to support card firmware upgraders who will need to > > > > disable suspend, even on (embedded) systems that don't have sysfs > > > > mounted. > > > > > > How do they enable autosuspend without sysfs? > > > > Good point. No idea. I suppose it's easier to rely on something that > > will always work, rather then making assumptions about suspend based on > > whether sysfs is mounted or not. > > Like I always like to point out, my _phone_ has sysfs mounted, I really > doubt any 2.6 embedded system would not have it mounted these days, it > is very useful for things like this. > > So I'll go delete that "special" ioctl, that's not a good thing to do, > use the common interfaces that all USB devices rely on instead, don't do > something different for just one type of USB device. Fair enough. > > I'm also a bit concerned about the special "set radio" ioctl as well. > Filip, what uses that ioctl? If it's really necessary, can't a sysfs > file do the same thing? Currently I don't think anybody uses it for Linux. I added it because our non-Linux drivers had this functionality. It's used for example by our connection managers/tools to disable the RF-part when the laptop goes to sleep ( but keeps the USB bus powered&suspended ). This is a requirement for quite some operators. But yes, I suppose the same could be done through sysfs. Regards, Filip- -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 16:24 ` Filip Aben @ 2008-04-15 17:58 ` Oliver Neukum 0 siblings, 0 replies; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 17:58 UTC (permalink / raw) To: Filip Aben; +Cc: Greg KH, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb Am Dienstag, 15. April 2008 18:24:48 schrieb Filip Aben: > > I'm also a bit concerned about the special "set radio" ioctl as well. > > Filip, what uses that ioctl? If it's really necessary, can't a sysfs > > file do the same thing? > > Currently I don't think anybody uses it for Linux. I added it because > our non-Linux drivers had this functionality. It's used for example by > our connection managers/tools to disable the RF-part when the laptop > goes to sleep ( but keeps the USB bus powered&suspended ). This is a > requirement for quite some operators. > But yes, I suppose the same could be done through sysfs. No, the correct way to do this is to support rfkill. I'll have a look at that. Regards Oliver ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 15:34 ` Greg KH [not found] ` <20080415153408.GB7996-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-15 18:46 ` Oliver Neukum [not found] ` <200804152046.44018.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 1 sibling, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 18:46 UTC (permalink / raw) To: Greg KH; +Cc: Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb Am Dienstag, 15. April 2008 17:34:08 schrieb Greg KH: > So I'll go delete that "special" ioctl, that's not a good thing to do, > use the common interfaces that all USB devices rely on instead, don't do > something different for just one type of USB device. Do you have a new unified patch? I'd work against that, lest we get merge troubles. Regards Oliver ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804152046.44018.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804152046.44018.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 19:00 ` Greg KH 2008-04-15 19:14 ` Stephen Hemminger ` (2 more replies) 0 siblings, 3 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 19:00 UTC (permalink / raw) To: Oliver Neukum Cc: Filip Aben, linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 08:46:42PM +0200, Oliver Neukum wrote: > Am Dienstag, 15. April 2008 17:34:08 schrieb Greg KH: > > So I'll go delete that "special" ioctl, that's not a good thing to do, > > use the common interfaces that all USB devices rely on instead, don't do > > something different for just one type of USB device. > > Do you have a new unified patch? I'd work against that, lest we get > merge troubles. Good idea :) Here it is. thanks, greg k-h ---------------- Subject: USB: add option hso driver This driver is for a number of different Option devices. Originally written by Option and Andrew Bird, but cleaned up massivly for acceptance into mainline by me (Greg). TODO: - remove proc files and move to debugfs - review network interfaces - add better changelog information Many thanks to the following for their help in cleaning up the driver by providing feedback and patches to it: - Paulius Zaleckas <paulius.zaleckas-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> - Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> - Alan Cox <alan-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org> Cc: Andrew Bird <ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io@public.gmane.org> Cc: Alan Cox <alan-qBU/x9rampVanCEyBjwyrvXRex20P6io@public.gmane.org> Cc: Filip Aben <f.aben-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org> Cc: Paulius Zaleckas <paulius.zaleckas-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> Cc: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> Signed-off-by: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org> --- drivers/net/usb/Kconfig | 10 drivers/net/usb/Makefile | 1 drivers/net/usb/hso.c | 3246 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3257 insertions(+) create mode 100644 drivers/net/usb/hso.c --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -154,6 +154,16 @@ config USB_NET_AX8817X This driver creates an interface named "ethX", where X depends on what other networking devices you have in use. +config USB_HSO + tristate "Option USB High Speed Mobile Devices" + depends on USB + default n + help + Choose this option if you have an Option HSDPA/HSUPA card. + These cards support downlink speeds of 7.2Mbps or greater. + + To compile this driver as a module, choose M here: the + module will be called hso. config USB_NET_CDCETHER tristate "CDC Ethernet support (smart devices such as cable modems)" --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_USB_CATC) += catc.o obj-$(CONFIG_USB_KAWETH) += kaweth.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RTL8150) += rtl8150.o +obj-$(CONFIG_USB_HSO) += hso.o obj-$(CONFIG_USB_NET_AX8817X) += asix.o obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o obj-$(CONFIG_USB_NET_DM9601) += dm9601.o --- /dev/null +++ b/drivers/net/usb/hso.c @@ -0,0 +1,3246 @@ +/****************************************************************************** + * + * Driver for Option High Speed Mobile Devices. + * + * Copyright (C) 2008 Option International + * Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd) + * <ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io@public.gmane.org> + * + * 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, should + * not be used. + * + *****************************************************************************/ + +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.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> +#include <linux/ip.h> +#include <linux/proc_fs.h> +#include <linux/uaccess.h> +#include <linux/usb/cdc.h> +#include <net/arp.h> +#include <asm/byteorder.h> + + +#define DRIVER_VERSION "1.2" +#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_INTR_BUF_SIZE 16 +#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_BUSY 1 +#define HSO_TERMINATE 2 + +#define HSO_NET_TX_TIMEOUT (HZ*10) + +/* Serial port defines and structs. */ +#define HSO_THRESHOLD_THROTTLE (7*1024) +#define HSO_THRESHOLD_UNTHROTTLE (2*1024) + +#define HSO_SERIAL_FLAG_THROTTLED 0 +#define HSO_SERIAL_FLAG_TX_SENT 1 +#define HSO_SERIAL_FLAG_RX_SENT 2 +#define HSO_SERIAL_USB_GONE 3 + +#define HSO_SERIAL_MAGIC 0x48534f31 + +/* Number of ttys to handle */ +#define HSO_SERIAL_TTY_MINORS 256 + +#define MAX_RX_URBS 2 + +#define get_serial_by_tty(x) \ + (x ? (struct hso_serial *)x->driver_data : 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; + spinlock_t shared_int_lock; +}; + +struct hso_net { + struct hso_device *parent; + struct net_device_stats stats; + struct net_device *net; + + 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; + struct ethhdr dummy_eth_head; + + __u16 bcdCDC; + __u16 wMaxSegmentSize; + __u16 wNumberMCFilters; + __u16 mode_flags; + unsigned long flags; + +}; + +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; + spinlock_t buffer_lock; + struct usb_ctrlrequest ctrl_req_tx; + struct usb_ctrlrequest ctrl_req_rx; + + struct usb_endpoint_descriptor *in_endp; + struct usb_endpoint_descriptor *out_endp; + + unsigned long flags; + 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; + void *private; + + int (*write_data) (struct hso_serial *serial); +}; + +struct hso_device { + union { + struct hso_serial *dev_serial; + struct hso_net *dev_net; + } port_data; + + u32 port_spec; + + u8 is_active; + u8 suspend_disabled; + struct work_struct async_get_intf; + struct work_struct async_put_intf; + + struct usb_device *usb; + struct usb_interface *interface; + + struct device *dev; + struct kref ref; + struct mutex mutex; + + /* TODO: Not sure about the ones below */ + struct proc_dir_entry *ourproc; +}; + +/* 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 */ +/*****************************************************************************/ +/* Network interface functions */ +static int hso_net_open(struct net_device *net); +static int hso_net_close(struct net_device *net); +static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net); +static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd); +static struct net_device_stats *hso_net_get_stats(struct net_device *net); +static void hso_net_tx_timeout(struct net_device *net); +static void hso_net_set_multicast(struct net_device *net); +static void read_bulk_callback(struct urb *urb); +static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, + unsigned int count, unsigned char is_eop); +static void write_bulk_callback(struct urb *urb); +/* 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(struct urb *urb); +static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial); +static void hso_std_serial_read_bulk_callback(struct urb *urb); +static void hso_std_serial_write_bulk_callback(struct urb *urb); +static void hso_kick_transmit(struct hso_serial *serial); +/* Base driver functions */ +static int hso_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void hso_disconnect(struct usb_interface *interface); +/* Helper functions */ +static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, + struct usb_device *usb, gfp_t gfp); +static void hso_net_init(struct net_device *net); +static void set_ethernet_addr(struct hso_net *odev); +static struct hso_serial *get_serial_by_index(unsigned index); +static struct hso_serial *get_serial_by_shared_int_and_type( + struct hso_shared_int *shared_int, + int mux); +static int get_free_serial_index(void); +static void set_serial_by_index(unsigned index, struct hso_serial *serial); +static int remove_net_device(struct hso_device *hso_dev); +static int add_net_device(struct hso_device *hso_dev); +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 int hso_suspend(struct usb_interface *iface, pm_message_t message); +static int hso_resume(struct usb_interface *iface); +static 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); + +/*****************************************************************************/ +/* Helping functions */ +/*****************************************************************************/ + +/* convert a character representing a hex value to a number */ +static unsigned char hex2dec(unsigned char digit) +{ + + if ((digit >= '0') && (digit <= '9')) + return (digit - '0'); + /* Map all characters to 0-15 */ + if ((digit >= 'a') && (digit <= 'z')) + return (digit - 'a' + 10) % 16; + if ((digit >= 'A') && (digit <= 'Z')) + return (digit - 'A' + 10) % 16; + return 16; +} + +#define SIOCSETRADIO (SIOCDEVPRIVATE+4) + +/* #define DEBUG */ + +#define dev2net(x) (x->port_data.dev_net) +#define dev2ser(x) (x->port_data.dev_serial) + +/* Debugging functions */ +#ifdef DEBUG +static void dbg_dump(int line_count, const char *func_name, unsigned char *buf, + unsigned int len) +{ + u8 i = 0; + + printk(KERN_DEBUG "[%d:%s]: len %d", line_count, func_name, len); + + for (i = 0; i < len; i++) { + if (!(i % 16)) + printk("\n 0x%03x: ", i); + printk("%02x ", (unsigned char)buf[i]); + } + printk("\n"); +} + +#define DUMP(buf_, len_) \ + dbg_dump(__LINE__, __func__, buf_, len_) + +#define DUMP1(buf_, len_) \ + do { \ + if (0x01 & debug) \ + DUMP(buf_, len_); \ + } while (0) +#else +#define DUMP(buf_, len_) +#define DUMP1(buf_, len_) +#endif + +/* module parameters */ +static int debug; +static int procfs = 1; +static int tty_major; +static int disable_net; +static int enable_autopm; + +/* driver info */ +static const char driver_name[] = "hso"; +static const char tty_filename[] = "ttyHS"; +static const char *version = __FILE__ ": " DRIVER_VERSION " " MOD_AUTHOR; +/* the usb driver itself (registered in hso_init) */ +static struct usb_driver hso_driver; +/* the procfs vars (if procfs enabled as parameter) */ +static struct proc_dir_entry *hso_proc_dir; +static struct proc_dir_entry *hso_proc_dir_devices; +/* serial structures */ +static struct tty_driver *tty_drv; +static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS]; +static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; +static spinlock_t serial_table_lock; +static struct ktermios *hso_serial_termios[HSO_SERIAL_TTY_MINORS]; +static struct ktermios *hso_serial_termios_locked[HSO_SERIAL_TTY_MINORS]; + +static const s32 default_port_spec[] = { + HSO_INTF_MUX | HSO_PORT_NETWORK, + HSO_INTF_BULK | HSO_PORT_DIAG, + HSO_INTF_BULK | HSO_PORT_MODEM, + 0 +}; + +static const s32 icon321_port_spec[] = { + HSO_INTF_MUX | HSO_PORT_NETWORK, + HSO_INTF_BULK | HSO_PORT_DIAG2, + HSO_INTF_BULK | HSO_PORT_MODEM, + HSO_INTF_BULK | HSO_PORT_DIAG, + 0 +}; + +#define default_port_device(vendor, product) \ + USB_DEVICE(vendor, product), \ + .driver_info = (kernel_ulong_t)default_port_spec + +#define icon321_port_device(vendor, product) \ + USB_DEVICE(vendor, product), \ + .driver_info = (kernel_ulong_t)icon321_port_spec + +/* list of devices we support */ +static struct usb_device_id hso_ids[] = { + {default_port_device(0x0af0, 0x6711)}, + {default_port_device(0x0af0, 0x6731)}, + {default_port_device(0x0af0, 0x6751)}, + {default_port_device(0x0af0, 0x6771)}, + {default_port_device(0x0af0, 0x6791)}, + {default_port_device(0x0af0, 0x6811)}, + {default_port_device(0x0af0, 0x6911)}, + {default_port_device(0x0af0, 0x6951)}, + {default_port_device(0x0af0, 0x6971)}, + {default_port_device(0x0af0, 0x7011)}, + {default_port_device(0x0af0, 0x7031)}, + {default_port_device(0x0af0, 0x7051)}, + {default_port_device(0x0af0, 0x7071)}, + {default_port_device(0x0af0, 0x7111)}, + {default_port_device(0x0af0, 0x7211)}, + {default_port_device(0x0af0, 0x7251)}, + {default_port_device(0x0af0, 0x7271)}, + {default_port_device(0x0af0, 0x7311)}, + {default_port_device(0x0af0, 0xc031)}, /* Icon-Edge */ + {icon321_port_device(0x0af0, 0xd013)}, /* Module HSxPA */ + {icon321_port_device(0x0af0, 0xd031)}, /* Icon-321 */ + {default_port_device(0x0af0, 0xd033)}, /* Icon-322 */ + {USB_DEVICE(0x0af0, 0x7301)}, /* GE40x */ + {USB_DEVICE(0x0af0, 0x7361)}, /* GE40x */ + {USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */ + {USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */ + {USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */ + {} +}; +MODULE_DEVICE_TABLE(usb, hso_ids); + +/* driver setup */ +static struct usb_driver hso_driver = { + .name = driver_name, + .probe = hso_probe, + .disconnect = hso_disconnect, + .id_table = hso_ids, + .suspend = hso_suspend, + .resume = hso_resume, + .supports_autosuspend = 1, +}; + +/* Sysfs attribute & function declaration */ +static ssize_t hso_sysfs_show_porttype(struct device *dev, + struct device_attribute *attr, + char *buf); +static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL); + +static ssize_t hso_sysfs_show_porttype(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hso_device *hso_dev = dev->driver_data; + char *port_name; + + if (!hso_dev) + return 0; + + switch (hso_dev->port_spec & HSO_PORT_MASK) { + case HSO_PORT_CONTROL: + port_name = "Control"; + break; + case HSO_PORT_APP: + port_name = "Application"; + break; + case HSO_PORT_APP2: + port_name = "Application2"; + break; + case HSO_PORT_GPS: + port_name = "GPS"; + break; + case HSO_PORT_GPS_CONTROL: + port_name = "GPS Control"; + break; + case HSO_PORT_PCSC: + port_name = "PCSC"; + break; + case HSO_PORT_DIAG: + port_name = "Diagnostic"; + break; + case HSO_PORT_DIAG2: + port_name = "Diagnostic2"; + break; + case HSO_PORT_MODEM: + port_name = "Modem"; + break; + case HSO_PORT_NETWORK: + port_name = "Network"; + break; + default: + port_name = "Unknown"; + break; + } + + return sprintf(buf, "%s\n", port_name); +} + +/* Procfs functions */ + +static int hso_proc_options(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + int len = 0; + /* get the module parameters */ + len += + snprintf(buf + len, count - len, "Version: %s\n", DRIVER_VERSION); + len += snprintf(buf + len, count - len, "debug: 0x%02x\n", debug); + len += snprintf(buf + len, count - len, "procfs: 0x%02x\n", procfs); + len += + snprintf(buf + len, count - len, "tty_major: 0x%02x\n", tty_major); + len += + snprintf(buf + len, count - len, "enable_autopm: 0x%02x\n", + enable_autopm); + len += + snprintf(buf + len, count - len, "disable_net: 0x%02x\n", + disable_net); + return len; +} + +static int hso_proc_port_info(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + int len = 0; + struct hso_device *hso_dev = (struct hso_device *)data; + char *port_name = NULL; + + D1("count: %d", count); + + switch (hso_dev->port_spec & HSO_PORT_MASK) { + case HSO_PORT_CONTROL: + port_name = "Control"; + break; + case HSO_PORT_APP: + port_name = "Application"; + break; + case HSO_PORT_GPS: + port_name = "GPS"; + break; + case HSO_PORT_GPS_CONTROL: + port_name = "GPS Control"; + break; + case HSO_PORT_APP2: + port_name = "Application2"; + break; + case HSO_PORT_PCSC: + port_name = "PCSC"; + break; + case HSO_PORT_DIAG: + port_name = "Diagnostic"; + break; + case HSO_PORT_DIAG2: + port_name = "Diagnostic2"; + break; + case HSO_PORT_MODEM: + port_name = "Modem"; + break; + case HSO_PORT_NETWORK: + port_name = "Network"; + break; + default: + port_name = "Unknown"; + break; + } + + len += snprintf(buf + len, count - len, "%s\n", port_name); + + /* return the device id to the user, for use in scripts for autopm */ + len += + snprintf(buf + len, count - len, "USB bus ID:\t%s\n", + hso_dev->usb->dev.bus_id); + return len; +} + +/* Network interface functions */ + +/* called when net interface is brought up by ifconfig */ +static int hso_net_open(struct net_device *net) +{ + struct hso_net *odev = netdev_priv(net); + unsigned long flags = 0; + + if (!odev) { + dev_err(&net->dev, "No net device !\n"); + return -ENODEV; + } + /* reset read counter. */ + odev->stats.rx_packets = 0; + odev->skb_tx_buf = NULL; + + /* setup environment */ + spin_lock_irqsave(&odev->net_lock, flags); + odev->rx_parse_state = WAIT_IP; + odev->rx_buf_size = 0; + odev->rx_buf_missing = sizeof(struct iphdr); + spin_unlock_irqrestore(&odev->net_lock, flags); + + hso_start_net_device(odev->parent); + + /* We are up and running. */ + set_bit(HSO_NET_RUNNING, &odev->flags); + + /* Tell the kernel we are ready to start receiving from it */ + netif_start_queue(net); + + return 0; +} + +/* called when interface is brought down by ifconfig */ +static int hso_net_close(struct net_device *net) +{ + struct hso_net *odev = netdev_priv(net); + + /* we don't need the queue anymore */ + netif_stop_queue(net); + /* no longer running */ + clear_bit(HSO_NET_RUNNING, &odev->flags); + + hso_stop_net_device(odev->parent); + + /* done */ + return 0; +} + +/* called by kernel when we need to transmit a packet */ +static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct hso_net *odev = netdev_priv(net); + int result = 0; + + /* Tell the kernel, "No more frames 'til we are done with this one." */ + netif_stop_queue(net); + if (hso_get_activity(odev->parent) == -EAGAIN) { + odev->skb_tx_buf = skb; + return 0; + } + + /* fetch the packet */ + skb_pull(skb, ETH_HLEN); + /* log if asked */ + DUMP1(skb->data, skb->len); + /* Copy it from kernel memory to OUR memory */ + memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len); + D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE); + + /* Fill in the URB for shipping it out. */ + usb_fill_bulk_urb(odev->mux_bulk_tx_urb, + odev->parent->usb, + usb_sndbulkpipe(odev->parent->usb, + odev->out_endp-> + bEndpointAddress & 0x7F), + odev->mux_bulk_tx_buf, skb->len, write_bulk_callback, + odev); + + /* Deal with the Zero Length packet problem, I hope */ + odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET; + + /* Send the URB on its merry way. */ + result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC); + if (result) { + dev_warn(&odev->parent->interface->dev, + "failed mux_bulk_tx_urb %d", result); + odev->stats.tx_errors++; + netif_start_queue(net); + } else { + odev->stats.tx_packets++; + odev->stats.tx_bytes += skb->len; + /* And tell the kernel when the last transmit started. */ + net->trans_start = jiffies; + } + dev_kfree_skb(skb); + /* we're done */ + return result; +} + +static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ + struct hso_net *odev = netdev_priv(net); + u32 usercmd = 0; + char tmp[40]; + + switch (cmd) { + case SIOCETHTOOL: + /* net specific */ + if (copy_from_user(&usercmd, rq->ifr_data, sizeof(usercmd))) + return -EFAULT; + + switch (usercmd) { + case ETHTOOL_GDRVINFO: + { + /* get driver info */ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRIVER_VERSION, + ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "usb%d:%d", + odev->parent->usb->bus->busnum, + odev->parent->usb->devnum); + strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "%s %x.%x", driver_name, + ((odev->bcdCDC & 0xff00) >> 8), + (odev->bcdCDC & 0x00ff)); + strncpy(info.fw_version, tmp, ETHTOOL_BUSINFO_LEN); + if (copy_to_user(rq->ifr_data, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + case ETHTOOL_GLINK: + { + /* get link status */ + struct ethtool_value edata = { ETHTOOL_GLINK }; + + edata.data = netif_carrier_ok(net); + if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) + return -EFAULT; + + return 0; + } + default: + dev_warn(&net->dev, "Got unsupported ioctl: %x\n", + usercmd); + /* the ethtool user space tool relies on this */ + return -EOPNOTSUPP; + } + default: + return -ENOTTY; /* per ioctl man page */ + } +} + +static struct net_device_stats *hso_net_get_stats(struct net_device *net) +{ + return &((struct hso_net *)netdev_priv(net))->stats; +} + +/* called when a packet did not ack after watchdogtimeout */ +static void hso_net_tx_timeout(struct net_device *net) +{ + struct hso_net *odev = netdev_priv(net); + + if (!odev) + return; + + /* Tell syslog we are hosed. */ + dev_warn(&net->dev, "Tx timed out.\n"); + + /* Tear the waiting frame off the list */ + if (odev->mux_bulk_tx_urb + && (odev->mux_bulk_tx_urb->status == -EINPROGRESS)) + usb_unlink_urb(odev->mux_bulk_tx_urb); + + /* Update statistics */ + odev->stats.tx_errors++; +} + +/* setup the multicast filters */ +static void hso_net_set_multicast(struct net_device *net) +{ + struct hso_net *odev = netdev_priv(net); + int i = 0; + __u8 *buf = NULL; + + /* Tell the kernel to stop sending us frames while we get this all set + * up. */ + netif_stop_queue(net); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (net->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + D1("%s: Promiscuous mode enabled", net->name); + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST | + USB_CDC_PACKET_TYPE_PROMISCUOUS; + } else if (net->mc_count > odev->wNumberMCFilters) { + /* Too many to filter perfectly -- accept all multicasts. */ + D1("%s: too many MC filters for hardware, using allmulti", + net->name); + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; + } else if (net->flags & IFF_ALLMULTI) { + /* Filter in software */ + D1("%s: using allmulti", net->name); + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; + } else { + /* do multicast filtering in hardware */ + struct dev_mc_list *mclist; + D1("%s: set multicast filters", net->name); + odev->mode_flags = USB_CDC_PACKET_TYPE_ALL_MULTICAST | + USB_CDC_PACKET_TYPE_DIRECTED | + USB_CDC_PACKET_TYPE_BROADCAST | + USB_CDC_PACKET_TYPE_MULTICAST; + buf = kmalloc(6 * net->mc_count, GFP_ATOMIC); + if (!buf) { + dev_err(&net->dev, "No memory to allocate?"); + goto exit; + } + for (i = 0, mclist = net->mc_list; mclist && i < net->mc_count; + i++, mclist = mclist->next) { + memcpy(&mclist->dmi_addr, &buf[i * 6], 6); + } + kfree(buf); + } + +exit: + /* Tell the kernel to start giving frames to us again. */ + netif_wake_queue(net); +} + +/* Moving data from usb to kernel (in interrupt state) */ +static void read_bulk_callback(struct urb *urb) +{ + struct hso_net *odev = urb->context; + struct net_device *net = NULL; + int result = 0; + int status = urb->status; + + /* is al ok? (Filip: Who's Al ?) */ + if (status) { + log_usb_status(status, __func__); + return; + } + + /* Sanity check */ + if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { + D1("BULK IN callback but driver is not active!"); + return; + } + usb_mark_last_busy(urb->dev); + + net = odev->net; + + if (!netif_device_present(net)) { + /* Somebody killed our network interface... */ + return; + } + + if (odev->parent->port_spec & HSO_INFO_CRC_BUG) { + u32 rest; + u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; + rest = urb->actual_length % odev->in_endp->wMaxPacketSize; + if (((rest == 5) || (rest == 6)) + && !memcmp(((u8 *) urb->transfer_buffer) + + urb->actual_length - 4, crc_check, 4)) { + urb->actual_length -= 4; + } + } + + /* do we even have a packet? */ + if (urb->actual_length) { + /* Handle the IP stream, add header and push it onto network + * stack if the packet is complete. */ + spin_lock(&odev->net_lock); + packetizeRx(odev, urb->transfer_buffer, urb->actual_length, + (urb->transfer_buffer_length > + urb->actual_length) ? 1 : 0); + spin_unlock(&odev->net_lock); + } + + /* We are done with this URB, resubmit it. Prep the USB to wait for + * another frame. Reuse same as received. */ + usb_fill_bulk_urb(urb, + odev->parent->usb, + usb_rcvbulkpipe(odev->parent->usb, + odev->in_endp-> + bEndpointAddress & 0x7F), + urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE, + read_bulk_callback, odev); + + /* Give this to the USB subsystem so it can tell us when more data + * arrives. */ + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_warn(&odev->parent->interface->dev, + "%s failed submit mux_bulk_rx_urb %d", __func__, + result); +} + +/* make a real packet from the received USB buffer */ +static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, + unsigned int count, unsigned char is_eop) +{ + unsigned short temp_bytes = 0; + unsigned short buffer_offset = 0; + unsigned short frame_len = 0; + unsigned char *tmp_rx_buf = NULL; + struct ethhdr *eth_head = NULL; + + /* log if needed */ + D1("Rx %d bytes", count); + DUMP(ip_pkt, min(128, (int)count)); + + while (count) { + switch (odev->rx_parse_state) { + case WAIT_IP: + /* waiting for IP header. */ + /* wanted bytes - size of ip header */ + temp_bytes = + (count < + odev->rx_buf_missing) ? count : odev-> + rx_buf_missing; + + memcpy(((unsigned char *)(&odev->rx_ip_hdr)) + + odev->rx_buf_size, ip_pkt + buffer_offset, + temp_bytes); + + odev->rx_buf_size += temp_bytes; + buffer_offset += temp_bytes; + odev->rx_buf_missing -= temp_bytes; + count -= temp_bytes; + + if (!odev->rx_buf_missing) { + /* header is complete allocate an sk_buffer and + * continue to WAIT_DATA */ + frame_len = ntohs(odev->rx_ip_hdr.tot_len); + + if ((frame_len > DEFAULT_MRU) || + (frame_len < sizeof(struct iphdr))) { + dev_err(&odev->net->dev, + "Invalid frame (%d) length\n", + frame_len); + odev->rx_parse_state = WAIT_SYNC; + continue; + } + /* Allocate an sk_buff */ + odev->skb_rx_buf = dev_alloc_skb( + frame_len + 2 + + odev->net->hard_header_len); + if (!odev->skb_rx_buf) { + /* We got no receive buffer. */ + D1("could not allocate memory"); + odev->rx_parse_state = WAIT_SYNC; + return; + } + /* Here's where it came from */ + odev->skb_rx_buf->dev = odev->net; + + /* Make some headroom: standard alignment + the + * ethernet header. */ + skb_reserve(odev->skb_rx_buf, + 2 + odev->net->hard_header_len); + + /* Copy what we got so far. make room for iphdr + * after tail. */ + tmp_rx_buf = + skb_put(odev->skb_rx_buf, + sizeof(struct iphdr)); + memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr), + sizeof(struct iphdr)); + + /* ETH_HLEN */ + odev->rx_buf_size = + odev->net->hard_header_len + + sizeof(struct iphdr); + + /* Filip actually use .tot_len */ + odev->rx_buf_missing = + frame_len - sizeof(struct iphdr); + odev->rx_parse_state = WAIT_DATA; + } + break; + + case WAIT_DATA: + temp_bytes = (count < odev->rx_buf_missing) + ? count : odev->rx_buf_missing; + + /* Copy the rest of the bytes that are left in the + * buffer into the waiting sk_buf. */ + /* Make room for temp_bytes after tail. */ + tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes); + memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes); + + odev->rx_buf_missing -= temp_bytes; + count -= temp_bytes; + buffer_offset += temp_bytes; + odev->rx_buf_size += temp_bytes; + if (!odev->rx_buf_missing) { + /* Packet is complete. Inject into stack. */ + /* Add fake ethernet header. */ + eth_head = (struct ethhdr *)skb_push(odev-> + skb_rx_buf, + odev-> + net-> + hard_header_len); + memcpy(eth_head, &odev->dummy_eth_head, + sizeof(struct ethhdr)); + + /* Not sure here either */ + odev->skb_rx_buf->protocol = eth_type_trans( + odev->skb_rx_buf, + odev->net); + /* don't check it */ + odev->skb_rx_buf->ip_summed = + CHECKSUM_UNNECESSARY; + /* Ship it off to the kernel */ + netif_rx(odev->skb_rx_buf); + /* No longer our buffer. */ + odev->skb_rx_buf = NULL; + + /* update out statistics */ + odev->stats.rx_packets++; + + /* Hmmm, wonder if we have received the IP len + * or the ETH len. */ + odev->stats.rx_bytes += odev->rx_buf_size; + + odev->rx_buf_size = 0; + odev->rx_buf_missing = sizeof(struct iphdr); + odev->rx_parse_state = WAIT_IP; + } + break; + + case WAIT_SYNC: + D1(" W_S"); + count = 0; + break; + default: + D1(" "); + count--; + break; + } + } + + /* Recovery mechanism for WAIT_SYNC state. */ + if (is_eop) { + if (odev->rx_parse_state == WAIT_SYNC) { + odev->rx_parse_state = WAIT_IP; + odev->rx_buf_size = 0; + odev->rx_buf_missing = sizeof(struct iphdr); + } + } +} + +/* USB tells is xmit done, we should start the netqueue again */ +static void write_bulk_callback(struct urb *urb) +{ + struct hso_net *odev = urb->context; + int status = urb->status; + + /* Sanity check */ + if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { + dev_err(&urb->dev->dev, "%s: device not running\n", __func__); + return; + } + + /* Do we still have a valid kernel network device? */ + if (!netif_device_present(odev->net)) { + dev_err(&urb->dev->dev, "%s: net device not present\n", + __func__); + return; + } + + /* log status, but don't act on it, we don't need to resubmit anything + * anyhow */ + if (status) + log_usb_status(status, __func__); + + hso_put_activity(odev->parent); + + /* Tell the network interface we are ready for another frame */ + netif_wake_queue(odev->net); +} + +/* Serial driver functions */ + +static void _hso_serial_set_termios(struct tty_struct *tty, + struct ktermios *old) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + struct ktermios *termios; + + if ((!tty) || (!tty->termios) || (!serial)) { + printk(KERN_ERR "%s: no tty structures", __func__); + return; + } + + D4("port %d", serial->minor); + + /* + * The default requirements for this device are: + */ + termios = serial->tty->termios; + termios->c_iflag &= + ~(IGNBRK /* disable ignore break */ + | BRKINT /* disable break causes interrupt */ + | PARMRK /* disable mark parity errors */ + | ISTRIP /* disable clear high bit of input characters */ + | INLCR /* disable translate NL to CR */ + | IGNCR /* disable ignore CR */ + | ICRNL /* disable translate CR to NL */ + | IXON); /* disable enable XON/XOFF flow control */ + + /* disable postprocess output characters */ + termios->c_oflag &= ~OPOST; + + termios->c_lflag &= + ~(ECHO /* disable echo input characters */ + | ECHONL /* disable echo new line */ + | ICANON /* disable erase, kill, werase, and rprnt + special characters */ + | ISIG /* disable interrupt, quit, and suspend special + characters */ + | IEXTEN); /* disable non-POSIX special characters */ + + termios->c_cflag &= + ~(CSIZE /* no size */ + | PARENB /* disable parity bit */ + | CBAUD /* clear current baud rate */ + | CBAUDEX); /* clear current buad rate */ + + termios->c_cflag |= CS8; /* character size 8 bits */ + + /* baud rate 115200 */ + tty_encode_baud_rate(serial->tty, 115200, 115200); + + /* + * Force low_latency on; otherwise the pushes are scheduled; + * this is bad as it opens up the possibility of dropping bytes + * on the floor. We don't want to drop bytes on the floor. :) + */ + serial->tty->low_latency = 1; + return; +} + +/* open the requested serial port */ +static int hso_serial_open(struct tty_struct *tty, struct file *filp) +{ + struct hso_serial *serial = get_serial_by_index(tty->index); + int result; + + /* sanity check */ + if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { + tty->driver_data = NULL; + D1("Failed to open port"); + return -ENODEV; + } + + mutex_lock(&serial->parent->mutex); + result = usb_autopm_get_interface(serial->parent->interface); + if (result < 0) + goto err_out; + + D1("Opening %d", serial->minor); + kref_get(&serial->parent->ref); + + /* setup */ + tty->driver_data = serial; + serial->tty = tty; + + /* check for port allready opened, if not set the termios */ + serial->open_count++; + if (serial->open_count == 1) { + tty->low_latency = 1; + serial->flags = 0; + /* Force default termio settings */ + _hso_serial_set_termios(tty, NULL); + result = hso_start_serial_device(serial->parent, GFP_KERNEL); + if (result) { + hso_stop_serial_device(serial->parent); + serial->open_count--; + kref_put(&serial->parent->ref, hso_serial_ref_free); + } + } else { + D1("Port was already open"); + } + + usb_autopm_put_interface(serial->parent->interface); + + /* done */ + if (result) + hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); +err_out: + mutex_unlock(&serial->parent->mutex); + return result; +} + +/* close the requested serial port */ +static void hso_serial_close(struct tty_struct *tty, struct file *filp) +{ + struct hso_serial *serial = tty->driver_data; + u8 usb_gone; + + D1("Closing serial port"); + + /* sanity check */ + if (tty == NULL || serial == NULL) { + D1("(tty == NULL || tty->driver_data == NULL)"); + return; + } + + mutex_lock(&serial->parent->mutex); + usb_gone = test_bit(HSO_SERIAL_USB_GONE, &serial->flags); + + if (!usb_gone) + usb_autopm_get_interface(serial->parent->interface); + + /* reset the rts and dtr */ + /* do the actual close */ + serial->open_count--; + if (serial->open_count <= 0) { + kref_put(&serial->parent->ref, hso_serial_ref_free); + serial->open_count = 0; + if (serial->tty) { + serial->tty->driver_data = NULL; + serial->tty = NULL; + } + if (!usb_gone) + hso_stop_serial_device(serial->parent); + } + if (!usb_gone) + usb_autopm_put_interface(serial->parent->interface); + mutex_unlock(&serial->parent->mutex); +} + +/* close the requested serial port */ +static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + int space = 0, tx_bytes = 0; + unsigned long flags; + + /* sanity check */ + if (serial == NULL) { + printk(KERN_ERR "%s: tty or tty->driver_data is NULL\n", + __func__); + return -ENODEV; + } + + spin_lock_irqsave(&serial->serial_lock, flags); + + space = serial->tx_data_length - serial->tx_buffer_count; + tx_bytes = (count < space) ? count : space; + + if (!tx_bytes) + goto out; + + memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes); + serial->tx_buffer_count += tx_bytes; + +out: + spin_unlock_irqrestore(&serial->serial_lock, flags); + + hso_kick_transmit(serial); + /* done */ + return tx_bytes; +} + +/* how much room is there for writing */ +static int hso_serial_write_room(struct tty_struct *tty) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + int room = 0; + unsigned long flags; + + spin_lock_irqsave(&serial->serial_lock, flags); + room = serial->tx_data_length - serial->tx_buffer_count; + spin_unlock_irqrestore(&serial->serial_lock, flags); + + /* return free room */ + return room; +} + +/* setup the term */ +static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + unsigned long flags; + + if (old) + D5("Termios called with: cflags new[%d] - old[%d]", + tty->termios->c_cflag, old->c_cflag); + + /* the actual setup */ + spin_lock_irqsave(&serial->serial_lock, flags); + if (serial->open_count) + _hso_serial_set_termios(tty, old); + spin_unlock_irqrestore(&serial->serial_lock, flags); + + /* done */ + return; +} + +/* how many characters in the buffer */ +static int hso_serial_chars_in_buffer(struct tty_struct *tty) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + int chars = 0; + unsigned long flags; + + /* sanity check */ + if (serial == NULL) + return 0; + + spin_lock_irqsave(&serial->serial_lock, flags); + chars = serial->tx_buffer_count; + spin_unlock_irqrestore(&serial->serial_lock, flags); + + return chars; +} + +static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file) +{ + unsigned int value = 0; + struct hso_serial *serial = get_serial_by_tty(tty); + unsigned long flags; + + /* sanity check */ + if (!serial) { + D1("no tty structures"); + return -EINVAL; + } + + spin_lock_irqsave(&serial->serial_lock, flags); + value = ((serial->rts_state) ? TIOCM_RTS : 0) | + ((serial->dtr_state) ? TIOCM_DTR : 0); + spin_unlock_irqrestore(&serial->serial_lock, flags); + + return value; +} + +static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + int val = 0; + unsigned long flags; + int if_num; + struct hso_serial *serial = get_serial_by_tty(tty); + + /* sanity check */ + if (!serial) { + D1("no tty structures"); + return -EINVAL; + } + if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber; + + spin_lock_irqsave(&serial->serial_lock, flags); + if (set & TIOCM_RTS) + serial->rts_state = 1; + if (set & TIOCM_DTR) + serial->dtr_state = 1; + + if (clear & TIOCM_RTS) + serial->rts_state = 0; + if (clear & TIOCM_DTR) + serial->dtr_state = 0; + + if (serial->dtr_state) + val |= 0x01; + if (serial->rts_state) + val |= 0x02; + + spin_unlock_irqrestore(&serial->serial_lock, flags); + + return usb_control_msg(serial->parent->usb, + usb_rcvctrlpipe(serial->parent->usb, 0), 0x22, + 0x21, val, if_num, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} + +/* Toggles radio on or off ( used by ioctl ) */ +static int hso_set_radio(struct hso_device *hso_dev, int enabled) +{ + if (!hso_dev) + return -ENODEV; + + return usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0), + enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} + +/* ioctl not supported */ +static int hso_serial_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hso_serial *serial = get_serial_by_tty(tty); + int ret = 0; + D4("IOCTL cmd: %d, arg: %ld", cmd, arg); + + if (!serial) + return -ENODEV; + + switch (cmd) { + case SIOCSETRADIO: + if (arg) + hso_set_radio(serial->parent, 1); + else + hso_set_radio(serial->parent, 0); + ret = 0; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} + +/* starts a transmit */ +static void hso_kick_transmit(struct hso_serial *serial) +{ + u8 *temp = NULL; + unsigned long flags; + int res; + + spin_lock_irqsave(&serial->serial_lock, flags); + if (!serial->tx_buffer_count) + goto out; + + if (serial->tx_urb_used) + goto out; + + /* Wakeup USB interface if necessary */ + if (hso_get_activity(serial->parent) == -EAGAIN) + goto out; + + /* Switch pointers around to avoid memcpy */ + temp = serial->tx_buffer; + serial->tx_buffer = serial->tx_data; + serial->tx_data = temp; + serial->tx_data_count = serial->tx_buffer_count; + serial->tx_buffer_count = 0; + + /* If temp is set, it means we switched buffers */ + if (temp && serial->write_data) { + res = serial->write_data(serial); + if (res >= 0) + serial->tx_urb_used = 1; + } +out: + spin_unlock_irqrestore(&serial->serial_lock, flags); +} + +/* converts mux value to a port spec value */ +static u32 hso_mux_to_port(int mux) +{ + u32 result; + + switch (mux) { + case 0x1: + result = HSO_PORT_CONTROL; + break; + case 0x2: + result = HSO_PORT_APP; + break; + case 0x4: + result = HSO_PORT_PCSC; + break; + case 0x8: + result = HSO_PORT_GPS; + break; + case 0x10: + result = HSO_PORT_APP2; + break; + default: + result = HSO_PORT_NO_PORT; + } + return result; +} + +/* converts port spec value to a mux value */ +static u32 hso_port_to_mux(int port) +{ + u32 result; + + switch (port & HSO_PORT_MASK) { + case HSO_PORT_CONTROL: + result = 0x0; + break; + case HSO_PORT_APP: + result = 0x1; + break; + case HSO_PORT_PCSC: + result = 0x2; + break; + case HSO_PORT_GPS: + result = 0x3; + break; + case HSO_PORT_APP2: + result = 0x4; + break; + default: + result = 0x0; + } + return result; +} + +/* make a request (for reading and writing data to muxed serial port) */ +static int mux_device_request(struct hso_serial *serial, u8 type, u16 port, + struct urb *ctrl_urb, + struct usb_ctrlrequest *ctrl_req, + u8 *ctrl_urb_data, u32 size) +{ + int result = 0; + int pipe = -1; + + /* Sanity check */ + if (!serial || !ctrl_urb || !ctrl_req) { + printk(KERN_ERR "%s: Wrong arguments\n", __func__); + return -EINVAL; + } + + /* initialize */ + ctrl_req->wValue = 0; + ctrl_req->wIndex = hso_port_to_mux(port); + ctrl_req->wLength = size; + + if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) { + /* Reading command */ + ctrl_req->bRequestType = USB_DIR_IN | + USB_TYPE_OPTION_VENDOR | + USB_RECIP_INTERFACE; + ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; + pipe = usb_rcvctrlpipe(serial->parent->usb, 0); + } else { + /* Writing command */ + ctrl_req->bRequestType = USB_DIR_OUT | + USB_TYPE_OPTION_VENDOR | + USB_RECIP_INTERFACE; + ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; + pipe = usb_sndctrlpipe(serial->parent->usb, 0); + } + /* syslog */ + D2("%s command (%02x) len: %d, port: %d", + type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write", + ctrl_req->bRequestType, ctrl_req->wLength, port); + + /* Load ctrl urb */ + ctrl_urb->transfer_flags = 0; + usb_fill_control_urb(ctrl_urb, + serial->parent->usb, + pipe, + (u8 *) ctrl_req, + ctrl_urb_data, size, ctrl_callback, serial); + /* Send it on merry way */ + result = usb_submit_urb(ctrl_urb, GFP_ATOMIC); + if (result) { + dev_err(&ctrl_urb->dev->dev, + "%s failed submit ctrl_urb %d type %d", __func__, + result, type); + return result; + } + + /* done */ + return size; +} + +/* called by intr_callback when read occurs */ +static int hso_mux_serial_read(struct hso_serial *serial) +{ + if (!serial) + return -EINVAL; + + /* clean data */ + memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE); + /* make the request */ + + if (serial->num_rx_urbs != 1) { + dev_err(&serial->parent->interface->dev, + "ERROR: mux'd reads with multiple buffers " + "not possible\n"); + return 0; + } + return mux_device_request(serial, + USB_CDC_GET_ENCAPSULATED_RESPONSE, + serial->parent->port_spec & HSO_PORT_MASK, + serial->rx_urb[0], + &serial->ctrl_req_rx, + serial->rx_data[0], serial->rx_data_length); +} + +/* used for muxed serial port callback (muxed serial read) */ +static void intr_callback(struct urb *urb) +{ + struct hso_shared_int *shared_int = urb->context; + struct hso_serial *serial = NULL; + unsigned char *port_req = NULL; + int status = urb->status; + int i = 0; + + usb_mark_last_busy(urb->dev); + + /* sanity check */ + if (!shared_int) + return; + + /* status check */ + if (status) { + log_usb_status(status, __func__); + return; + } + D4("\n--- Got intr callback 0x%02X ---", status); + + /* what request? */ + port_req = urb->transfer_buffer; + D4(" port_req = 0x%.2X\n", *port_req); + /* loop over all muxed ports to find the one sending this */ + for (i = 0; i < 8; i++) { + /* max 8 channels on MUX */ + if (*port_req & (1 << i)) { + serial = get_serial_by_shared_int_and_type(shared_int, + (1 << i)); + if (serial != NULL) { + D1("Pending read interrupt on port %d\n", i); + if (!test_and_set_bit(HSO_SERIAL_FLAG_RX_SENT, + &serial->flags)) { + /* Setup and send a ctrl req read on + * port i */ + hso_mux_serial_read(serial); + } else { + D1("Already pending a read on " + "port %d\n", i); + } + } + } + } + /* Resubmit interrupt urb */ + hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_KERNEL); +} + +/* called for writing to muxed serial port */ +static int hso_mux_serial_write_data(struct hso_serial *serial) +{ + if (NULL == serial) + return -EINVAL; + + return mux_device_request(serial, + USB_CDC_SEND_ENCAPSULATED_COMMAND, + serial->parent->port_spec & HSO_PORT_MASK, + serial->tx_urb, + &serial->ctrl_req_tx, + serial->tx_data, serial->tx_data_count); +} + +/* called for writing diag or CS serial port */ +static int hso_std_serial_write_data(struct hso_serial *serial) +{ + int count = serial->tx_data_count; + int result = 0; + + usb_fill_bulk_urb(serial->tx_urb, + serial->parent->usb, + usb_sndbulkpipe(serial->parent->usb, + serial->out_endp-> + bEndpointAddress & 0x7F), + serial->tx_data, serial->tx_data_count, + hso_std_serial_write_bulk_callback, serial); + + result = usb_submit_urb(serial->tx_urb, GFP_KERNEL); + if (result) { + dev_warn(&serial->parent->usb->dev, + "Failed to submit urb - res %d\n", result); + return result; + } + + return count; +} + +/* callback after read or write on muxed serial port */ +static void ctrl_callback(struct urb *urb) +{ + struct hso_serial *serial = urb->context; + struct usb_ctrlrequest *req = NULL; + int status = urb->status; + + /* sanity check */ + if (!serial) + return; + + spin_lock(&serial->serial_lock); + serial->tx_urb_used = 0; + spin_unlock(&serial->serial_lock); + if (status) { + log_usb_status(status, __func__); + return; + } + + /* what request? */ + req = (struct usb_ctrlrequest *)(urb->setup_packet); + D4("\n--- Got muxed ctrl callback 0x%02X ---", status); + D4("Actual length of urb = %d\n", urb->actual_length); + DUMP1(urb->transfer_buffer, urb->actual_length); + + if (req->bRequestType == + (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) { + /* response to a read command */ + if (serial->open_count > 0) { + /* handle RX data the normal way */ + put_rxbuf_data(urb, serial); + } + + /* Re issue a read as long as we receive data. */ + if (urb->actual_length != 0) + hso_mux_serial_read(serial); + else + clear_bit(HSO_SERIAL_FLAG_RX_SENT, &serial->flags); + } else { + hso_put_activity(serial->parent); + tty_wakeup(serial->tty); + /* response to a write command */ + hso_kick_transmit(serial); + } +} + +/* handle RX data for serial port */ +static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial) +{ + struct tty_struct *tty = serial->tty; + + /* Sanity check */ + if (urb == NULL || serial == NULL) { + D1("serial = NULL"); + return; + } + + /* Push data to tty */ + if (tty && urb->actual_length) { + D1("data to push to tty"); + tty_insert_flip_string(tty, urb->transfer_buffer, + urb->actual_length); + tty_flip_buffer_push(tty); + } +} + +/* read callback for Diag and CS port */ +static void hso_std_serial_read_bulk_callback(struct urb *urb) +{ + struct hso_serial *serial = urb->context; + int result = 0; + int status = urb->status; + + /* sanity check */ + if (!serial) { + D1("serial == NULL"); + return; + } else if (status) { + log_usb_status(status, __func__); + return; + } + + D4("\n--- Got serial_read_bulk callback %02x ---", status); + D1("Actual length = %d\n", urb->actual_length); + DUMP1(urb->transfer_buffer, urb->actual_length); + + /* Anyone listening? */ + if (serial->open_count == 0) + return; + + if (status == 0) { + if (serial->parent->port_spec & HSO_INFO_CRC_BUG) { + u32 rest; + u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; + rest = + urb->actual_length % + serial->in_endp->wMaxPacketSize; + if (((rest == 5) || (rest == 6)) + && !memcmp(((u8 *) urb->transfer_buffer) + + urb->actual_length - 4, crc_check, 4)) { + urb->actual_length -= 4; + } + } + /* Valid data, handle RX data */ + put_rxbuf_data(urb, serial); + } else if (status == -ENOENT || status == -ECONNRESET) { + /* Unlinked - check for throttled port. */ + D2("Port %d, successfully unlinked urb", serial->minor); + } else { + D2("Port %d, status = %d for read urb", serial->minor, status); + return; + } + + usb_mark_last_busy(urb->dev); + + /* We are done with this URB, resubmit it. Prep the USB to wait for + * another frame */ + usb_fill_bulk_urb(urb, serial->parent->usb, + usb_rcvbulkpipe(serial->parent->usb, + serial->in_endp-> + bEndpointAddress & 0x7F), + urb->transfer_buffer, serial->rx_data_length, + hso_std_serial_read_bulk_callback, serial); + /* Give this to the USB subsystem so it can tell us when more data + * arrives. */ + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) { + dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d", + __func__, result); + } +} + +/* write callback for Diag and CS port */ +static void hso_std_serial_write_bulk_callback(struct urb *urb) +{ + struct hso_serial *serial = urb->context; + int status = urb->status; + + /* sanity check */ + if (!serial) { + D1("serial == NULL"); + return; + } + + spin_lock(&serial->serial_lock); + serial->tx_urb_used = 0; + spin_unlock(&serial->serial_lock); + if (status) { + log_usb_status(status, __func__); + return; + } + hso_put_activity(serial->parent); + tty_wakeup(serial->tty); + hso_kick_transmit(serial); + + D1(" "); + return; +} + +/* Base driver functions */ + +/* operations setup of the serial interface */ +static struct tty_operations hso_serial_ops = { + .open = hso_serial_open, + .close = hso_serial_close, + .write = hso_serial_write, + .write_room = hso_serial_write_room, + .ioctl = hso_serial_ioctl, + .set_termios = hso_serial_set_termios, + .chars_in_buffer = hso_serial_chars_in_buffer, + .tiocmget = hso_serial_tiocmget, + .tiocmset = hso_serial_tiocmset, +}; + +static int __init hso_init(void) +{ + int i = 0; + int result = 0; + + /* put it in the log */ + printk(KERN_INFO "hso: %s\n", version); + + /* if procfs is enabled, initialize devices */ + if (procfs) { + hso_proc_dir = proc_mkdir(driver_name, NULL); + if (hso_proc_dir) { + hso_proc_dir_devices = + proc_mkdir("devices", hso_proc_dir); + create_proc_read_entry("options", 0, hso_proc_dir, + hso_proc_options, NULL); + } + } + + /* Initialise the serial table semaphore and table */ + spin_lock_init(&serial_table_lock); + for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) + serial_table[i] = NULL; + + /* allocate our driver using the proper amount of supported minors */ + tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS); + if (!tty_drv) + return -ENOMEM; + + /* fill in all needed values */ + tty_drv->magic = TTY_DRIVER_MAGIC; + tty_drv->owner = THIS_MODULE; + tty_drv->driver_name = driver_name; + tty_drv->name = tty_filename; + + /* if major number is provided as parameter, use that one */ + if (tty_major) + tty_drv->major = tty_major; + + tty_drv->minor_start = 0; + tty_drv->num = HSO_SERIAL_TTY_MINORS; + tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + tty_drv->subtype = SERIAL_TYPE_NORMAL; + tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_drv->init_termios = tty_std_termios; + tty_drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_drv->termios = hso_serial_termios; + tty_drv->termios_locked = hso_serial_termios_locked; + tty_set_operations(tty_drv, &hso_serial_ops); + + /* register the tty driver */ + result = tty_register_driver(tty_drv); + if (result) { + printk(KERN_ERR "%s - tty_register_driver failed(%d)\n", + __func__, result); + return result; + } + + /* register this module as an usb driver */ + result = usb_register(&hso_driver); + if (result) { + printk(KERN_ERR "Could not register hso driver? error: %d\n", + result); + /* cleanup serial interface */ + tty_unregister_driver(tty_drv); + return result; + } + + /* done */ + return 0; +} + +static void __exit hso_exit(void) +{ + printk(KERN_INFO "hso: unloaded\n"); + + tty_unregister_driver(tty_drv); + /* deregister the usb driver */ + usb_deregister(&hso_driver); + /* if procfs is enabled, remove the entries */ + + if (procfs) { + remove_proc_entry("options", hso_proc_dir); + remove_proc_entry("devices", hso_proc_dir); + remove_proc_entry(driver_name, NULL); + } + +} + +static void hso_log_port(struct hso_device *hso_dev) +{ + char *port_type = NULL; + char port_dev[20]; + + switch (hso_dev->port_spec & HSO_PORT_MASK) { + case HSO_PORT_CONTROL: + port_type = "Control"; + break; + case HSO_PORT_APP: + port_type = "Application"; + break; + case HSO_PORT_GPS: + port_type = "GPS"; + break; + case HSO_PORT_GPS_CONTROL: + port_type = "GPS control"; + break; + case HSO_PORT_APP2: + port_type = "Application2"; + break; + case HSO_PORT_PCSC: + port_type = "PCSC"; + break; + case HSO_PORT_DIAG: + port_type = "Diagnostic"; + break; + case HSO_PORT_DIAG2: + port_type = "Diagnostic2"; + break; + case HSO_PORT_MODEM: + port_type = "Modem"; + break; + case HSO_PORT_NETWORK: + port_type = "Network"; + break; + default: + port_type = "Unknown"; + break; + } + if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { + sprintf(port_dev, "%s", dev2net(hso_dev)->net->name); + } else + sprintf(port_dev, "/dev/%s%d", tty_filename, + dev2ser(hso_dev)->minor); + + dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n", + port_type, port_dev); +} + +static int hso_start_net_device(struct hso_device *hso_dev) +{ + int i, result = 0; + struct hso_net *hso_net = dev2net(hso_dev); + + if (!hso_net) + return -ENODEV; + + /* send URBs for all read buffers */ + for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { + + /* Prep a receive URB */ + usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i], + hso_dev->usb, + usb_rcvbulkpipe(hso_dev->usb, + hso_net->in_endp-> + bEndpointAddress & 0x7F), + hso_net->mux_bulk_rx_buf_pool[i], + MUX_BULK_RX_BUF_SIZE, read_bulk_callback, + hso_net); + + /* Put it out there so the device can send us stuff */ + result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i], + GFP_KERNEL); + if (result) + dev_warn(&hso_dev->usb->dev, + "%s failed mux_bulk_rx_urb[%d] %d\n", __func__, + i, result); + } + + return result; +} + +static int hso_stop_net_device(struct hso_device *hso_dev) +{ + int i; + struct hso_net *hso_net = dev2net(hso_dev); + + if (!hso_net) + return -ENODEV; + + for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { + if (hso_net->mux_bulk_rx_urb_pool[i]) + usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]); + + } + if (hso_net->mux_bulk_tx_urb) + usb_kill_urb(hso_net->mux_bulk_tx_urb); + + return 0; +} + +static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) +{ + int i, result = 0; + struct hso_serial *serial = dev2ser(hso_dev); + + if (!serial) + return -ENODEV; + + /* If it is not the MUX port fill in and submit a bulk urb (already + * allocated in hso_serial_start) */ + if (!(serial->parent->port_spec & HSO_INTF_MUX)) { + for (i = 0; i < serial->num_rx_urbs; i++) { + usb_fill_bulk_urb(serial->rx_urb[i], + serial->parent->usb, + usb_rcvbulkpipe(serial->parent->usb, + serial->in_endp-> + bEndpointAddress & + 0x7F), + serial->rx_data[i], + serial->rx_data_length, + hso_std_serial_read_bulk_callback, + serial); + result = usb_submit_urb(serial->rx_urb[i], flags); + if (result) { + dev_warn(&serial->parent->usb->dev, + "Failed to submit urb - res %d\n", + result); + break; + } + } + } else { + spin_lock_bh(&serial->shared_int->shared_int_lock); + if (!serial->shared_int->use_count) { + result = + hso_mux_submit_intr_urb(serial->shared_int, + hso_dev->usb, GFP_KERNEL); + } + serial->shared_int->use_count++; + spin_unlock_bh(&serial->shared_int->shared_int_lock); + } + + return result; +} + +static int hso_stop_serial_device(struct hso_device *hso_dev) +{ + int i; + struct hso_serial *serial = dev2ser(hso_dev); + + if (!serial) + return -ENODEV; + + for (i = 0; i < serial->num_rx_urbs; i++) { + if (serial->rx_urb[i]) + usb_kill_urb(serial->rx_urb[i]); + } + + if (serial->tx_urb) + usb_kill_urb(serial->rx_urb[i]); + + if (serial->shared_int) { + spin_lock_bh(&serial->shared_int->shared_int_lock); + if (serial->shared_int->use_count && + (--serial->shared_int->use_count == 0)) { + struct urb *urb; + + urb = serial->shared_int->shared_intr_urb; + if (urb) + usb_kill_urb(urb); + } + spin_unlock_bh(&serial->shared_int->shared_int_lock); + } + + return 0; +} + +static void hso_serial_common_free(struct hso_serial *serial) +{ + int i; + + if (serial->parent->dev) + device_remove_file(serial->parent->dev, &dev_attr_hsotype); + + tty_unregister_device(tty_drv, serial->minor); + + for (i = 0; i < serial->num_rx_urbs; i++) { + /* unlink and free RX URB */ + usb_free_urb(serial->rx_urb[i]); + /* free the RX buffer */ + kfree(serial->rx_data[i]); + } + + /* unlink and free TX URB */ + usb_free_urb(serial->tx_urb); + kfree(serial->tx_data); +} + +static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, + int rx_size, int tx_size) +{ + struct device *dev = NULL; + int minor; + int i; + + minor = get_free_serial_index(); + if (minor < 0) + goto exit; + + /* register our minor number */ + serial->parent->dev = tty_register_device(tty_drv, minor, + &serial->parent->interface->dev); + dev = serial->parent->dev; + dev->driver_data = serial->parent; + i = device_create_file(dev, &dev_attr_hsotype); + + /* fill in specific data for later use */ + serial->minor = minor; + serial->magic = HSO_SERIAL_MAGIC; + spin_lock_init(&serial->serial_lock); + serial->num_rx_urbs = num_urbs; + + /* RX, allocate urb and initialize */ + + /* prepare our RX buffer */ + serial->rx_data_length = rx_size; + for (i = 0; i < serial->num_rx_urbs; i++) { + serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!serial->rx_urb[i]) { + dev_err(dev, "Could not allocate urb?\n"); + goto exit; + } + serial->rx_urb[i]->transfer_buffer = NULL; + serial->rx_urb[i]->transfer_buffer_length = 0; + serial->rx_data[i] = kzalloc(serial->rx_data_length, + GFP_KERNEL); + if (!serial->rx_data[i]) { + dev_err(dev, "%s - Out of memory\n", __func__); + goto exit; + } + } + + /* TX, allocate urb and initialize */ + serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!serial->tx_urb) { + dev_err(dev, "Could not allocate urb?\n"); + goto exit; + } + serial->tx_urb->transfer_buffer = NULL; + serial->tx_urb->transfer_buffer_length = 0; + /* prepare our TX buffer */ + serial->tx_data_count = 0; + serial->tx_buffer_count = 0; + serial->tx_data_length = tx_size; + serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL); + if (!serial->tx_data) { + dev_err(dev, "%s - Out of memory", __func__); + goto exit; + } + serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL); + if (!serial->tx_buffer) { + dev_err(dev, "%s - Out of memory", __func__); + goto exit; + } + + return 0; +exit: + hso_serial_common_free(serial); + return -1; + +} + +/* Frees a general hso device */ +static void hso_free_device(struct hso_device *hso_dev) +{ + kfree(hso_dev); +} + +/* Creates a general hso device */ +static struct hso_device *hso_create_device(struct usb_interface *intf, + int port_spec) +{ + struct hso_device *hso_dev; + + hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC); + if (!hso_dev) + return NULL; + + hso_dev->port_spec = port_spec; + hso_dev->usb = interface_to_usbdev(intf); + hso_dev->interface = intf; + kref_init(&hso_dev->ref); + mutex_init(&hso_dev->mutex); + + INIT_WORK(&hso_dev->async_get_intf, async_get_intf); + INIT_WORK(&hso_dev->async_put_intf, async_put_intf); + + return hso_dev; +} + +/* Frees our network device */ +static void hso_free_net_device(struct hso_device *hso_dev) +{ + int i; + struct hso_net *hso_net = dev2net(hso_dev); + + if (!hso_net) + return; + + if (procfs) + remove_proc_entry(hso_net->net->name, hso_proc_dir_devices); + + /* start freeing */ + for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { + usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]); + kfree(hso_net->mux_bulk_rx_buf_pool[i]); + } + usb_free_urb(hso_net->mux_bulk_tx_urb); + kfree(hso_net->mux_bulk_tx_buf); + + remove_net_device(hso_net->parent); + + if (hso_net->net) { + unregister_netdev(hso_net->net); + free_netdev(hso_net->net); + } + + hso_free_device(hso_dev); +} + +/* Creates our network device */ +static struct hso_device *hso_create_net_device(struct usb_interface *interface) +{ + int result, i; + struct net_device *net = NULL; + struct hso_net *hso_net = NULL; + struct hso_device *hso_dev = NULL; + + hso_dev = hso_create_device(interface, HSO_INTF_MUX | HSO_PORT_NETWORK); + if (!hso_dev) + return NULL; + + /* allocate our network device, then we can put in our private data */ + /* call hso_net_init to do the basic initialization */ + net = alloc_netdev(sizeof(struct hso_net), "hso%d", hso_net_init); + if (!net) { + dev_err(&interface->dev, "Unable to create ethernet device\n"); + goto exit; + } + + hso_net = netdev_priv(net); + + hso_dev->port_data.dev_net = hso_net; + hso_net->net = net; + hso_net->parent = hso_dev; + + hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, + USB_DIR_IN); + if (!hso_net->in_endp) { + dev_err(&interface->dev, "Can't find BULK IN endpoint\n"); + goto exit; + } + hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, + USB_DIR_OUT); + if (!hso_net->out_endp) { + dev_err(&interface->dev, "Can't find BULK OUT endpoint\n"); + goto exit; + } + SET_NETDEV_DEV(net, &interface->dev); + + /* registering our net device */ + result = register_netdev(net); + if (result) { + dev_err(&interface->dev, "Failed to register device\n"); + goto exit; + } + + /* start allocating */ + for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { + hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!hso_net->mux_bulk_rx_urb_pool[i]) { + dev_err(&interface->dev, "Could not allocate rx urb\n"); + goto exit; + } + hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE, + GFP_KERNEL); + if (!hso_net->mux_bulk_rx_buf_pool[i]) { + dev_err(&interface->dev, "Could not allocate rx buf\n"); + goto exit; + } + } + hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!hso_net->mux_bulk_tx_urb) { + dev_err(&interface->dev, "Could not allocate tx urb\n"); + goto exit; + } + hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL); + if (!hso_net->mux_bulk_tx_buf) { + dev_err(&interface->dev, "Could not allocate tx buf\n"); + goto exit; + } + + /* and don't forget the MAC address. */ + set_ethernet_addr(hso_net); + + add_net_device(hso_dev); + + /* setup the proc dirs and files if needed */ + if (procfs) { + if (!hso_proc_dir_devices) + printk(KERN_WARNING "hso: Warning, creating port info " + "under root\n"); + create_proc_read_entry(hso_net->net->name, 0, + hso_proc_dir_devices, hso_proc_port_info, + hso_dev); + } + + hso_log_port(hso_dev); + + return hso_dev; +exit: + hso_free_net_device(hso_dev); + return NULL; +} + +/* Frees an AT channel ( goes for both mux and non-mux ) */ +static void hso_free_serial_device(struct hso_device *hso_dev) +{ + struct hso_serial *serial = dev2ser(hso_dev); + char device_name[20]; + + if (!serial) + return; + set_serial_by_index(serial->minor, NULL); + + if (procfs) { + sprintf(device_name, "%s%d", tty_filename, serial->minor); + remove_proc_entry(device_name, hso_proc_dir_devices); + } + + hso_serial_common_free(serial); + + if (serial->shared_int) { + spin_lock_bh(&serial->shared_int->shared_int_lock); + if (--serial->shared_int->ref_count == 0) + hso_free_shared_int(serial->shared_int); + spin_unlock_bh(&serial->shared_int->shared_int_lock); + } + kfree(serial); + hso_free_device(hso_dev); +} + +/* Creates a bulk AT channel */ +static struct hso_device *hso_create_bulk_serial_device( + struct usb_interface *interface, int port) +{ + struct hso_device *hso_dev = NULL; + struct hso_serial *serial = NULL; + char device_name[20]; + int num_urbs; + + hso_dev = hso_create_device(interface, port); + if (!hso_dev) + return NULL; + + serial = kzalloc(sizeof(*serial), GFP_KERNEL); + if (!serial) + goto exit; + + serial->parent = hso_dev; + hso_dev->port_data.dev_serial = serial; + + if (port & HSO_PORT_MODEM) + num_urbs = 2; + else + num_urbs = 1; + + if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE, + BULK_URB_TX_SIZE)) + goto exit; + + serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, + USB_DIR_IN); + if (!serial->in_endp) { + dev_err(&interface->dev, "Failed to find BULK IN ep\n"); + goto exit; + } + + if (! + (serial->out_endp = + hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) { + dev_err(&interface->dev, "Failed to find BULK IN ep\n"); + goto exit; + } + + serial->write_data = hso_std_serial_write_data; + + /* and record this serial */ + set_serial_by_index(serial->minor, serial); + + /* setup the proc dirs and files if needed */ + if (procfs) { + if (!hso_proc_dir_devices) + printk(KERN_WARNING "hso: Warning, creating port " + "info under root\n"); + sprintf(device_name, "%s%d", tty_filename, serial->minor); + create_proc_read_entry(device_name, 0, hso_proc_dir_devices, + hso_proc_port_info, hso_dev); + } + + hso_log_port(hso_dev); + + /* done, return it */ + return hso_dev; +exit: + if (hso_dev && serial) + hso_serial_common_free(serial); + kfree(serial); + hso_free_device(hso_dev); + return NULL; +} + +/* Creates a multiplexed AT channel */ +static +struct hso_device *hso_create_mux_serial_device(struct usb_interface *interface, + int port, + struct hso_shared_int *mux) +{ + struct hso_device *hso_dev = NULL; + struct hso_serial *serial = NULL; + char device_name[20]; + int port_spec = 0; + + port_spec |= HSO_INTF_MUX; + port_spec &= ~HSO_PORT_MASK; + + port_spec |= hso_mux_to_port(port); + if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT) + return NULL; + + hso_dev = hso_create_device(interface, port_spec); + if (!hso_dev) + return NULL; + + serial = kzalloc(sizeof(*serial), GFP_KERNEL); + if (!serial) + goto exit; + + hso_dev->port_data.dev_serial = serial; + serial->parent = hso_dev; + + if (hso_serial_common_create + (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE)) + goto exit; + + serial->tx_data_length--; + serial->write_data = hso_mux_serial_write_data; + + serial->shared_int = mux; + spin_lock_bh(&serial->shared_int->shared_int_lock); + serial->shared_int->ref_count++; + spin_unlock_bh(&serial->shared_int->shared_int_lock); + + /* and record this serial */ + set_serial_by_index(serial->minor, serial); + + /* setup the proc dirs and files if needed */ + if (procfs) { + if (!hso_proc_dir_devices) + printk(KERN_WARNING "hso: Warning, creating port info " + "under root\n"); + sprintf(device_name, "%s%d", tty_filename, serial->minor); + create_proc_read_entry(device_name, 0, hso_proc_dir_devices, + hso_proc_port_info, hso_dev); + } + + hso_log_port(hso_dev); + + /* done, return it */ + return hso_dev; + +exit: + if (serial) { + tty_unregister_device(tty_drv, serial->minor); + kfree(serial); + } + if (hso_dev) + hso_free_device(hso_dev); + return NULL; + +} + +static void hso_free_shared_int(struct hso_shared_int *mux) +{ + usb_free_urb(mux->shared_intr_urb); + kfree(mux->shared_intr_buf); + kfree(mux); +} + +static +struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface) +{ + struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL); + + if (!mux) + return NULL; + + mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT, + USB_DIR_IN); + if (!mux->intr_endp) { + dev_err(&interface->dev, "Can't find INT IN endpoint\n"); + goto exit; + } + + mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mux->shared_intr_urb) { + dev_err(&interface->dev, "Could not allocate intr urb?"); + goto exit; + } + mux->shared_intr_buf = kzalloc(mux->intr_endp->wMaxPacketSize, + GFP_KERNEL); + if (!mux->shared_intr_buf) { + dev_err(&interface->dev, "Could not allocate intr buf?"); + goto exit; + } + + spin_lock_init(&mux->shared_int_lock); + + return mux; + +exit: + kfree(mux->shared_intr_buf); + usb_free_urb(mux->shared_intr_urb); + kfree(mux); + return NULL; +} + +/* Gets the port spec for a certain interface */ +static int hso_get_config_data(struct usb_interface *interface) +{ + struct usb_device *usbdev = interface_to_usbdev(interface); + u8 config_data[17]; + u32 if_num = interface->altsetting->desc.bInterfaceNumber; + s32 result = 0; + + if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + 0x86, 0xC0, 0, 0, config_data, 17, + USB_CTRL_SET_TIMEOUT) != 0x11) { + return -EIO; + } + + switch (config_data[if_num]) { + case 0x0: + result = 0; + break; + case 0x1: + result = HSO_PORT_DIAG; + break; + case 0x2: + result = HSO_PORT_GPS; + break; + case 0x3: + result = HSO_PORT_GPS_CONTROL; + break; + case 0x4: + result = HSO_PORT_APP; + break; + case 0x5: + result = HSO_PORT_APP2; + break; + case 0x6: + result = HSO_PORT_CONTROL; + break; + case 0x7: + result = HSO_PORT_NETWORK; + break; + case 0x8: + result = HSO_PORT_MODEM; + break; + case 0x9: + result = HSO_PORT_MSD; + break; + case 0xa: + result = HSO_PORT_PCSC; + break; + case 0xb: + result = HSO_PORT_VOICE; + break; + default: + result = 0; + } + + if (result) + result |= HSO_INTF_BULK; + + if (config_data[16] & 0x1) + result |= HSO_INFO_CRC_BUG; + + return result; +} + +/* called once for each interface upon device insertion */ +static int hso_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int mux, i, if_num, port_spec; + unsigned char port_mask; + struct hso_device *hso_dev = NULL; + struct hso_shared_int *shared_int = NULL; + struct hso_device *tmp_dev = NULL; + + if_num = interface->altsetting->desc.bInterfaceNumber; + + /* Get the interface/port specification from either driver_info or from + * the device itself */ + if (id->driver_info) + port_spec = ((u32 *)(id->driver_info))[if_num]; + else + port_spec = hso_get_config_data(interface); + + if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { + dev_err(&interface->dev, "Not our interface\n"); + return -ENODEV; + } + /* Check if we need to switch to alt interfaces prior to port + * configuration */ + if (interface->num_altsetting > 1) + usb_set_interface(interface_to_usbdev(interface), if_num, 1); + interface->needs_remote_wakeup = 1; + + /* Allocate new hso device(s) */ + switch (port_spec & HSO_INTF_MASK) { + case HSO_INTF_MUX: + if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { + /* Create the network device */ + if (!disable_net) { + hso_dev = hso_create_net_device(interface); + if (!hso_dev) + goto exit; + tmp_dev = hso_dev; + } + } + + if (hso_get_mux_ports(interface, &port_mask)) + /* TODO: de-allocate everything */ + goto exit; + + shared_int = hso_create_shared_int(interface); + if (!shared_int) + goto exit; + + for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) { + if (port_mask & i) { + hso_dev = hso_create_mux_serial_device( + interface, i, shared_int); + if (!hso_dev) + goto exit; + } + } + + if (tmp_dev) + hso_dev = tmp_dev; + break; + + case HSO_INTF_BULK: + /* It's a regular bulk interface */ + if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) + && !disable_net) + hso_dev = hso_create_net_device(interface); + else + hso_dev = + hso_create_bulk_serial_device(interface, port_spec); + if (!hso_dev) + goto exit; + break; + default: + goto exit; + } + + usb_driver_claim_interface(&hso_driver, interface, hso_dev); + + /* save our data pointer in this device */ + usb_set_intfdata(interface, hso_dev); + + if (enable_autopm == 0) { + usb_autopm_get_interface(hso_dev->interface); + hso_dev->suspend_disabled = 1; + } + + /* done */ + return 0; + +exit: + hso_free_interface(interface); + return -ENODEV; +} + +/* device removed, cleaning up */ +static void hso_disconnect(struct usb_interface *interface) +{ + hso_free_interface(interface); + + /* remove reference of our private data */ + usb_set_intfdata(interface, NULL); + + usb_driver_release_interface(&hso_driver, interface); +} + +static void async_get_intf(struct work_struct *data) +{ + struct hso_device *hso_dev = + container_of(data, struct hso_device, async_get_intf); + usb_autopm_get_interface(hso_dev->interface); +} + +static void async_put_intf(struct work_struct *data) +{ + struct hso_device *hso_dev = + container_of(data, struct hso_device, async_put_intf); + usb_autopm_put_interface(hso_dev->interface); +} + +static int hso_get_activity(struct hso_device *hso_dev) +{ + if (hso_dev->usb->state == USB_STATE_SUSPENDED) { + if (!hso_dev->is_active) { + hso_dev->is_active = 1; + schedule_work(&hso_dev->async_get_intf); + } + } + + if (hso_dev->usb->state != USB_STATE_CONFIGURED) + return -EAGAIN; + + usb_mark_last_busy(hso_dev->usb); + + return 0; +} + +static int hso_put_activity(struct hso_device *hso_dev) +{ + if (hso_dev->usb->state != USB_STATE_SUSPENDED) { + if (hso_dev->is_active) { + hso_dev->is_active = 0; + schedule_work(&hso_dev->async_put_intf); + return -EAGAIN; + } + } + hso_dev->is_active = 0; + return 0; +} + +/* called by kernel when we need to suspend device */ +static int hso_suspend(struct usb_interface *iface, pm_message_t message) +{ + int i, result = 0; + + /* Stop all serial ports */ + for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { + if (serial_table[i] && (serial_table[i]->interface == iface)) { + result = hso_stop_serial_device(serial_table[i]); + if (result) + goto out; + } + } + + /* Stop all network ports */ + for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { + if (network_table[i] && + (network_table[i]->interface == iface)) { + result = hso_stop_net_device(network_table[i]); + if (result) + goto out; + } + } + +out: + return 0; +} + +/* called by kernel when we need to resume device */ +static int hso_resume(struct usb_interface *iface) +{ + int i, result = 0; + struct hso_net *hso_net; + + /* Start all serial ports */ + for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { + if (serial_table[i] && (serial_table[i]->interface == iface)) { + if (dev2ser(serial_table[i])->open_count) { + result = + hso_start_serial_device(serial_table[i], GFP_NOIO); + hso_kick_transmit(dev2ser(serial_table[i])); + if (result) + goto out; + } + } + } + + /* Start all network ports */ + for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { + if (network_table[i] && + (network_table[i]->interface == iface)) { + hso_net = dev2net(network_table[i]); + /* First transmit any lingering data, then restart the + * device. */ + if (hso_net->skb_tx_buf) { + dev_dbg(&iface->dev, + "Transmitting lingering data\n"); + hso_net_start_xmit(hso_net->skb_tx_buf, + hso_net->net); + } + result = hso_start_net_device(network_table[i]); + if (result) + goto out; + } + } + +out: + return result; +} + +static void hso_serial_ref_free(struct kref *ref) +{ + struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); + + hso_free_serial_device(hso_dev); +} + +static void hso_free_interface(struct usb_interface *interface) +{ + struct hso_serial *hso_dev; + int i; + + for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { + if (serial_table[i] + && (serial_table[i]->interface == interface)) { + hso_dev = dev2ser(serial_table[i]); + if (hso_dev->tty) + tty_hangup(hso_dev->tty); + mutex_lock(&hso_dev->parent->mutex); + set_bit(HSO_SERIAL_USB_GONE, &hso_dev->flags); + mutex_unlock(&hso_dev->parent->mutex); + kref_put(&serial_table[i]->ref, hso_serial_ref_free); + } + } + + for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { + if (network_table[i] + && (network_table[i]->interface == interface)) { + /* hso_stop_net_device doesn't stop the net queue since + * traffic needs to start it again when suspended */ + netif_stop_queue(dev2net(network_table[i])->net); + hso_stop_net_device(network_table[i]); + hso_free_net_device(network_table[i]); + } + } +} + +/* Helper functions */ + +/* Get the endpoint ! */ +static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, + int type, int dir) +{ + int i; + struct usb_host_interface *iface = intf->cur_altsetting; + struct usb_endpoint_descriptor *endp = NULL; + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + endp = &iface->endpoint[i].desc; + if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) && + ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type)) + return endp; + } + + return NULL; +} + +/* Get the byte that describes which ports are enabled */ +static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports) +{ + int i; + struct usb_host_interface *iface = intf->cur_altsetting; + + if (iface->extralen == 3) { + *ports = iface->extra[2]; + return 0; + } + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + if (iface->endpoint[i].extralen == 3) { + *ports = iface->endpoint[i].extra[2]; + return 0; + } + } + + return -1; +} + +/* interrupt urb needs to be submitted, used for serial read of muxed port */ +static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int, + struct usb_device *usb, gfp_t gfp) +{ + int result; + + usb_fill_int_urb(shared_int->shared_intr_urb, usb, + usb_rcvintpipe(usb, + shared_int->intr_endp->bEndpointAddress & 0x7F), + shared_int->shared_intr_buf, + shared_int->intr_endp->wMaxPacketSize, + intr_callback, shared_int, + shared_int->intr_endp->bInterval); + + result = usb_submit_urb(shared_int->shared_intr_urb, gfp); + if (result) + dev_warn(&usb->dev, "%s failed mux_intr_urb %d", __func__, + result); + + return result; +} + +/* initialize the network interface */ +static void hso_net_init(struct net_device *net) +{ + struct hso_net *hso_net = netdev_priv(net); + + /* initialize to zero */ + memset(hso_net, 0, sizeof(*hso_net)); + D1("sizeof hso_net is %d", (int)sizeof(*hso_net)); + + /* most of the setup is done by standard function */ + ether_setup(net); + + /* fill in the other fields */ + net->open = hso_net_open; + net->stop = hso_net_close; + net->hard_start_xmit = hso_net_start_xmit; + net->do_ioctl = hso_net_ioctl; + net->get_stats = hso_net_get_stats; + net->tx_timeout = hso_net_tx_timeout; + net->watchdog_timeo = HSO_NET_TX_TIMEOUT; + net->set_multicast_list = hso_net_set_multicast; + net->flags |= IFF_NOARP; + net->mtu = DEFAULT_MTU - 14; + net->tx_queue_len = 10; + hso_net->skb_rx_buf = NULL; + hso_net->rx_parse_state = WAIT_IP; + hso_net->wMaxSegmentSize = DEFAULT_MTU; + /* always use all multi, no lists of multicasts */ + hso_net->wNumberMCFilters = 0; + + /* and initialize the semaphore */ + spin_lock_init(&hso_net->net_lock); +} + +/* setting the mac-address of the newly created iface */ +static void set_ethernet_addr(struct hso_net *odev) +{ + unsigned char mac_addr[6]; + unsigned char dummy_mac[6]; + int i = 0; + int len = 0; + unsigned char buffer[13]; + unsigned char checkserial[7] = "Serial"; + + /* we can't fail, therefor we use a random macaddress */ + random_ether_addr(mac_addr); + dummy_mac[0] = 0xFA; + dummy_mac[1] = mac_addr[1]; + dummy_mac[2] = mac_addr[2]; + dummy_mac[3] = mac_addr[3]; + dummy_mac[4] = mac_addr[4]; + dummy_mac[5] = mac_addr[5]; + + /* but we like consistency, that's why we try to use the serial number + * as a macaddress if available */ + len = usb_string(odev->parent->usb, + odev->parent->usb->descriptor.iSerialNumber, + buffer, 13); + if (len != 12) { + /* some devices don't have the serial filled in, checking ... */ + for (i = 0; i < 6; i++) { + if (buffer[i] != checkserial[i]) { + /* Fill in the mac_addr */ + for (i = 2; i < 6; i++) { + if ((16 == buffer[2 * i]) || + (16 == buffer[2 * i + 1])) { + dev_err(&odev->parent->usb->dev, + "Bad value in MAC " + "address i:%d\n", i); + } else { + mac_addr[i] = + (hex2dec(buffer[2 * i]) << + 4) + hex2dec(buffer[2 * i + + 1]); + } + } + break; + } + } + } else { + dev_err(&odev->parent->usb->dev, "Attempting to get MAC " + "address failed: using random\n"); + } + + /* Now copy it over to our network device structure */ + memcpy(odev->net->dev_addr, mac_addr, sizeof(mac_addr)); + + /* Create the default fake ethernet header. */ + memcpy(odev->dummy_eth_head.h_dest, mac_addr, ETH_ALEN); + memcpy(odev->dummy_eth_head.h_source, dummy_mac, ETH_ALEN); + odev->dummy_eth_head.h_proto = htons(ETH_P_IP); +} + +/* Adds a network device in the network device table */ +static int add_net_device(struct hso_device *hso_dev) +{ + int i; + + for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { + if (network_table[i] == NULL) { + network_table[i] = hso_dev; + break; + } + } + if (i == HSO_MAX_NET_DEVICES) + return -1; + return 0; +} + +/* Removes a network device in the network device table */ +static int remove_net_device(struct hso_device *hso_dev) +{ + int i; + + for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { + if (network_table[i] == hso_dev) { + network_table[i] = NULL; + break; + } + } + if (i == HSO_MAX_NET_DEVICES) + return -1; + return 0; +} + +static struct hso_serial *get_serial_by_shared_int_and_type(struct + hso_shared_int + *shared_int, + int mux) +{ + int i, port; + + port = hso_mux_to_port(mux); + + for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { + if (serial_table[i] + && (dev2ser(serial_table[i])->shared_int == shared_int) + && ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) { + return dev2ser(serial_table[i]); + } + } + + return NULL; +} + +static struct hso_serial *get_serial_by_index(unsigned index) +{ + struct hso_serial *serial = NULL; + unsigned long flags; + + if (!serial_table[index]) + return NULL; + spin_lock_irqsave(&serial_table_lock, flags); + serial = dev2ser(serial_table[index]); + spin_unlock_irqrestore(&serial_table_lock, flags); + + return serial; +} + +static int get_free_serial_index(void) +{ + int index = 0; + unsigned long flags; + + spin_lock_irqsave(&serial_table_lock, flags); + for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) { + if (serial_table[index] == NULL) { + spin_unlock_irqrestore(&serial_table_lock, flags); + return index; + } + } + spin_unlock_irqrestore(&serial_table_lock, flags); + + printk(KERN_ERR "%s: no free serial devices in table\n", __func__); + return -1; +} + +static void set_serial_by_index(unsigned index, struct hso_serial *serial) +{ + unsigned long flags; + spin_lock_irqsave(&serial_table_lock, flags); + if (serial) + serial_table[index] = serial->parent; + else + serial_table[index] = NULL; + spin_unlock_irqrestore(&serial_table_lock, flags); +} + +/* log a meaningfull explanation of an USB status to syslog */ +static void log_usb_status(int status, const char *function) +{ + char *explanation = NULL; + + switch (status) { + case -ENODEV: + explanation = "no device"; + break; + case -ENOENT: + explanation = "endpoint not enabled"; + break; + case -EPIPE: + explanation = "endpoint stalled"; + break; + case -ENOSPC: + explanation = "not enough bandwidth"; + break; + case -ESHUTDOWN: + explanation = "device disabled"; + break; + case -EHOSTUNREACH: + explanation = "device suspended"; + break; + case -EINVAL: + case -EAGAIN: + case -EFBIG: + case -EMSGSIZE: + explanation = "internal error"; + break; + default: + explanation = "unknown status"; + break; + } + D1("%s: received USB status - %s (%d)", function, explanation, status); +} + +/* Module definitions */ +module_init(hso_init); +module_exit(hso_exit); + +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESCRIPTION); +MODULE_LICENSE(MOD_LICENSE); +MODULE_INFO(Version, DRIVER_VERSION); + +/* Module parameter to allow procfs (eg: insmod hso.ko procfs=1) */ +MODULE_PARM_DESC(procfs, "Allow procfs [0 | 1]"); +module_param(procfs, int, S_IRUGO | S_IWUSR); + +/* change the debug level (eg: insmod hso.ko debug=0x04) */ +MODULE_PARM_DESC(debug, "Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10]"); +module_param(debug, int, S_IRUGO | S_IWUSR); + +/* set the major tty number (eg: insmod hso.ko tty_major=245) */ +MODULE_PARM_DESC(tty_major, "Set the major tty number"); +module_param(tty_major, int, S_IRUGO | S_IWUSR); + +/* set the major tty number (eg: insmod hso.ko tty_major=245) */ +MODULE_PARM_DESC(disable_net, "Disable the network interface"); +module_param(disable_net, int, S_IRUGO | S_IWUSR); + +/* enable autosuspend (default disabled) (eg: insmod hso.ko enable_autopm=1) */ +MODULE_PARM_DESC(enable_autopm, + "Enable the auto power managment (autosuspend)"); +module_param(enable_autopm, int, S_IRUGO | S_IWUSR); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 19:00 ` Greg KH @ 2008-04-15 19:14 ` Stephen Hemminger 2008-04-15 19:27 ` Greg KH 2008-04-15 20:17 ` Oliver Neukum 2008-04-17 12:15 ` Oliver Neukum 2 siblings, 1 reply; 54+ messages in thread From: Stephen Hemminger @ 2008-04-15 19:14 UTC (permalink / raw) To: Greg KH Cc: Oliver Neukum, Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb On Tue, 15 Apr 2008 12:00:30 -0700 Greg KH <greg@kroah.com> wrote: > On Tue, Apr 15, 2008 at 08:46:42PM +0200, Oliver Neukum wrote: > > Am Dienstag, 15. April 2008 17:34:08 schrieb Greg KH: > > > So I'll go delete that "special" ioctl, that's not a good thing to do, > > > use the common interfaces that all USB devices rely on instead, don't do > > > something different for just one type of USB device. > > > > Do you have a new unified patch? I'd work against that, lest we get > > merge troubles. > > Good idea :) > > Here it is. > > thanks, > > greg k-h > > ---------------- > > Subject: USB: add option hso driver > > This driver is for a number of different Option devices. Originally > written by Option and Andrew Bird, but cleaned up massivly for > acceptance into mainline by me (Greg). > > TODO: > - remove proc files and move to debugfs > - review network interfaces > - add better changelog information More work left: - Use netif_msg_ for the message level rather than module parameter - net_device_stats are now available in dev->stats - use ethtool_ops rather than ioctl ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 19:14 ` Stephen Hemminger @ 2008-04-15 19:27 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 19:27 UTC (permalink / raw) To: Stephen Hemminger Cc: Oliver Neukum, Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb On Tue, Apr 15, 2008 at 12:14:12PM -0700, Stephen Hemminger wrote: > > TODO: > > - remove proc files and move to debugfs > > - review network interfaces > > - add better changelog information > > More work left: > > - Use netif_msg_ for the message level rather than module parameter > - net_device_stats are now available in dev->stats > - use ethtool_ops rather than ioctl Thanks, I've added them to the TODO list. greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 19:00 ` Greg KH 2008-04-15 19:14 ` Stephen Hemminger @ 2008-04-15 20:17 ` Oliver Neukum [not found] ` <200804152217.25451.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-17 12:15 ` Oliver Neukum 2 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 20:17 UTC (permalink / raw) To: Greg KH; +Cc: Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb Am Dienstag, 15. April 2008 21:00:30 schrieb Greg KH: > > Do you have a new unified patch? I'd work against that, lest we get > > merge troubles. > > Good idea :) > > Here it is. Against that one. Change GFP_KERNEL used in interrupt and resume() to GFP_ATOMIC, respectively GFP_NOIO Signed-off-by: Oliver Neukum <oneukum@suse.de> --- --- a/drivers/net/usb/hso.c 2008-04-15 21:48:10.000000000 +0200 +++ b/drivers/net/usb/hso.c 2008-04-15 21:52:48.000000000 +0200 @@ -1663,7 +1663,7 @@ static void intr_callback(struct urb *ur } } /* Resubmit interrupt urb */ - hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_KERNEL); + hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC); } /* called for writing to muxed serial port */ @@ -1694,7 +1694,7 @@ static int hso_std_serial_write_data(str serial->tx_data, serial->tx_data_count, hso_std_serial_write_bulk_callback, serial); - result = usb_submit_urb(serial->tx_urb, GFP_KERNEL); + result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC); if (result) { dev_warn(&serial->parent->usb->dev, "Failed to submit urb - res %d\n", result); @@ -2040,7 +2040,7 @@ static int hso_start_net_device(struct h /* Put it out there so the device can send us stuff */ result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i], - GFP_KERNEL); + GFP_NOIO); if (result) dev_warn(&hso_dev->usb->dev, "%s failed mux_bulk_rx_urb[%d] %d\n", __func__, @@ -2104,7 +2104,7 @@ static int hso_start_serial_device(struc if (!serial->shared_int->use_count) { result = hso_mux_submit_intr_urb(serial->shared_int, - hso_dev->usb, GFP_KERNEL); + hso_dev->usb, GFP_ATOMIC); } serial->shared_int->use_count++; spin_unlock_bh(&serial->shared_int->shared_int_lock); ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804152217.25451.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804152217.25451.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-15 22:18 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 22:18 UTC (permalink / raw) To: Oliver Neukum Cc: Filip Aben, linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 10:17:21PM +0200, Oliver Neukum wrote: > Am Dienstag, 15. April 2008 21:00:30 schrieb Greg KH: > > > Do you have a new unified patch? I'd work against that, lest we get > > > merge troubles. > > > > Good idea :) > > > > Here it is. > > Against that one. > > Change GFP_KERNEL used in interrupt and resume() to GFP_ATOMIC, > respectively GFP_NOIO > > Signed-off-by: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org> Thanks, now applied. greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 19:00 ` Greg KH 2008-04-15 19:14 ` Stephen Hemminger 2008-04-15 20:17 ` Oliver Neukum @ 2008-04-17 12:15 ` Oliver Neukum 2008-04-17 21:35 ` Greg KH 2 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-17 12:15 UTC (permalink / raw) To: Greg KH; +Cc: Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb Am Dienstag, 15. April 2008 21:00:30 schrieb Greg KH: > On Tue, Apr 15, 2008 at 08:46:42PM +0200, Oliver Neukum wrote: > > Am Dienstag, 15. April 2008 17:34:08 schrieb Greg KH: > > > So I'll go delete that "special" ioctl, that's not a good thing to do, > > > use the common interfaces that all USB devices rely on instead, don't do > > > something different for just one type of USB device. > > > > Do you have a new unified patch? I'd work against that, lest we get > > merge troubles. > > Good idea :) > > Here it is. Another one. As autosuspend is by default disabled, it makes no sense to have a module parameter disabling it. Just don't enable it. Regards Oliver Signed-off-by: Oliver Neukum <oneukum@suse.de> --- --- linux-2.6.25hso/drivers/net/usb/hso.c.alt 2008-04-17 14:00:09.000000000 +0200 +++ linux-2.6.25hso/drivers/net/usb/hso.c 2008-04-17 14:01:43.000000000 +0200 @@ -400,7 +400,6 @@ static int debug; static int procfs = 1; static int tty_major; static int disable_net; -static int enable_autopm; /* driver info */ static const char driver_name[] = "hso"; @@ -555,9 +554,6 @@ static int hso_proc_options(char *buf, c len += snprintf(buf + len, count - len, "tty_major: 0x%02x\n", tty_major); len += - snprintf(buf + len, count - len, "enable_autopm: 0x%02x\n", - enable_autopm); - len += snprintf(buf + len, count - len, "disable_net: 0x%02x\n", disable_net); return len; @@ -2718,11 +2714,6 @@ static int hso_probe(struct usb_interfac /* save our data pointer in this device */ usb_set_intfdata(interface, hso_dev); - if (enable_autopm == 0) { - usb_autopm_get_interface(hso_dev->interface); - hso_dev->suspend_disabled = 1; - } - /* done */ return 0; @@ -3213,8 +3204,3 @@ module_param(tty_major, int, S_IRUGO | S /* set the major tty number (eg: insmod hso.ko tty_major=245) */ MODULE_PARM_DESC(disable_net, "Disable the network interface"); module_param(disable_net, int, S_IRUGO | S_IWUSR); - -/* enable autosuspend (default disabled) (eg: insmod hso.ko enable_autopm=1) */ -MODULE_PARM_DESC(enable_autopm, - "Enable the auto power managment (autosuspend)"); -module_param(enable_autopm, int, S_IRUGO | S_IWUSR); ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-17 12:15 ` Oliver Neukum @ 2008-04-17 21:35 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-17 21:35 UTC (permalink / raw) To: Oliver Neukum Cc: Filip Aben, linux-usb, netdev, Alan Cox, Paulius Zaleckas, ajb On Thu, Apr 17, 2008 at 02:15:06PM +0200, Oliver Neukum wrote: > Am Dienstag, 15. April 2008 21:00:30 schrieb Greg KH: > > On Tue, Apr 15, 2008 at 08:46:42PM +0200, Oliver Neukum wrote: > > > Am Dienstag, 15. April 2008 17:34:08 schrieb Greg KH: > > > > So I'll go delete that "special" ioctl, that's not a good thing to do, > > > > use the common interfaces that all USB devices rely on instead, don't do > > > > something different for just one type of USB device. > > > > > > Do you have a new unified patch? I'd work against that, lest we get > > > merge troubles. > > > > Good idea :) > > > > Here it is. > > Another one. > > As autosuspend is by default disabled, it makes no sense to have a module > parameter disabling it. Just don't enable it. Applied, thanks. greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> ` (4 preceding siblings ...) 2008-04-15 13:25 ` Oliver Neukum @ 2008-04-15 22:49 ` Paulius Zaleckas [not found] ` <480530E6.8020700-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 2008-04-16 13:11 ` Paulius Zaleckas 6 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-15 22:49 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 173 bytes --] Reworked MAC address generation from USB serial number Now it is very similar to the one done in the cdc_ether.c Filip: Does the dummy MAC address have to start with 0xFA? [-- Attachment #2: hso_mac_gen.patch --] [-- Type: text/x-patch, Size: 2356 bytes --] --- linux-2.6-hso/drivers/net/usb/hso.c 2008-04-15 23:56:33.000000000 +0300 +++ linux-2.6-hso-my/drivers/net/usb/hso.c 2008-04-16 01:22:56.000000000 +0300 @@ -345,7 +345,6 @@ static int hso_get_activity(struct hso_d /* convert a character representing a hex value to a number */ static unsigned char hex2dec(unsigned char digit) { - if ((digit >= '0') && (digit <= '9')) return (digit - '0'); /* Map all characters to 0-15 */ @@ -353,7 +352,7 @@ static unsigned char hex2dec(unsigned ch return (digit - 'a' + 10) % 16; if ((digit >= 'A') && (digit <= 'Z')) return (digit - 'A' + 10) % 16; - return 16; + return 0; } #define SIOCSETRADIO (SIOCDEVPRIVATE+4) @@ -3032,7 +3031,6 @@ static void set_ethernet_addr(struct hso int i = 0; int len = 0; unsigned char buffer[13]; - unsigned char checkserial[7] = "Serial"; /* we can't fail, therefor we use a random macaddress */ random_ether_addr(mac_addr); @@ -3048,26 +3046,11 @@ static void set_ethernet_addr(struct hso len = usb_string(odev->parent->usb, odev->parent->usb->descriptor.iSerialNumber, buffer, 13); - if (len != 12) { - /* some devices don't have the serial filled in, checking ... */ - for (i = 0; i < 6; i++) { - if (buffer[i] != checkserial[i]) { - /* Fill in the mac_addr */ - for (i = 2; i < 6; i++) { - if ((16 == buffer[2 * i]) || - (16 == buffer[2 * i + 1])) { - dev_err(&odev->parent->usb->dev, - "Bad value in MAC " - "address i:%d\n", i); - } else { - mac_addr[i] = - (hex2dec(buffer[2 * i]) << - 4) + hex2dec(buffer[2 * i + - 1]); - } - } - break; - } + if (len != 12 && strcmp("Serial", buffer)) { + /* Fill in the mac_addr */ + for (i = 1; i < 6; i++) { + mac_addr[i] = (hex2dec(buffer[2 * i]) << 4) + + hex2dec(buffer[2 * i + 1]); } } else { dev_err(&odev->parent->usb->dev, "Attempting to get MAC " @@ -3236,7 +3219,7 @@ module_param(debug, int, S_IRUGO | S_IWU MODULE_PARM_DESC(tty_major, "Set the major tty number"); module_param(tty_major, int, S_IRUGO | S_IWUSR); -/* set the major tty number (eg: insmod hso.ko tty_major=245) */ +/* disable network interface (eg: insmod hso.ko disable_net=1) */ MODULE_PARM_DESC(disable_net, "Disable the network interface"); module_param(disable_net, int, S_IRUGO | S_IWUSR); ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <480530E6.8020700-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <480530E6.8020700-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-16 9:18 ` Paulius Zaleckas [not found] ` <4805C469.7050408-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 9:18 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 331 bytes --] Paulius Zaleckas wrote: > Reworked MAC address generation from USB serial number > Now it is very similar to the one done in the cdc_ether.c > > Filip: Does the dummy MAC address have to start with 0xFA? > Hopefully fixed last bug in MAC address generation. Patch against latest Greg unified patch. Don't use my previous patch. [-- Attachment #2: hso_mac_gen_2.patch --] [-- Type: text/x-patch, Size: 2481 bytes --] --- linux-2.6-hso/drivers/net/usb/hso.c 2008-04-16 10:58:01.000000000 +0300 +++ linux-2.6-hso-my/drivers/net/usb/hso.c 2008-04-16 12:06:19.000000000 +0300 @@ -315,7 +315,6 @@ static int hso_get_activity(struct hso_d /* convert a character representing a hex value to a number */ static unsigned char hex2dec(unsigned char digit) { - if ((digit >= '0') && (digit <= '9')) return (digit - '0'); /* Map all characters to 0-15 */ @@ -323,7 +322,7 @@ static unsigned char hex2dec(unsigned ch return (digit - 'a' + 10) % 16; if ((digit >= 'A') && (digit <= 'Z')) return (digit - 'A' + 10) % 16; - return 16; + return 0; } #define SIOCSETRADIO (SIOCDEVPRIVATE+4) @@ -2308,7 +2307,6 @@ static void set_ethernet_addr(struct hso int i = 0; int len = 0; unsigned char buffer[13]; - unsigned char checkserial[7] = "Serial"; /* we can't fail, therefor we use a random macaddress */ random_ether_addr(mac_addr); @@ -2324,27 +2322,14 @@ static void set_ethernet_addr(struct hso len = usb_string(odev->parent->usb, odev->parent->usb->descriptor.iSerialNumber, buffer, 13); - if (len != 12) { - /* some devices don't have the serial filled in, checking ... */ + if (len != 12 && strcmp("Serial", buffer)) { + /* Fill in the mac_addr */ for (i = 0; i < 6; i++) { - if (buffer[i] != checkserial[i]) { - /* Fill in the mac_addr */ - for (i = 2; i < 6; i++) { - if ((16 == buffer[2 * i]) || - (16 == buffer[2 * i + 1])) { - dev_err(&odev->parent->usb->dev, - "Bad value in MAC " - "address i:%d\n", i); - } else { - mac_addr[i] = - (hex2dec(buffer[2 * i]) << - 4) + hex2dec(buffer[2 * i + - 1]); - } - } - break; - } + mac_addr[i] = (hex2dec(buffer[2 * i]) << 4) + + hex2dec(buffer[2 * i + 1]); } + mac_addr[0] &= 0xfe; /* clear multicast bit */ + mac_addr[0] |= 0x02; /* set local assignment bit (IEEE802) */ } else { dev_err(&odev->parent->usb->dev, "Attempting to get MAC " "address failed: using random\n"); @@ -3200,7 +3185,7 @@ module_param(debug, int, S_IRUGO | S_IWU MODULE_PARM_DESC(tty_major, "Set the major tty number"); module_param(tty_major, int, S_IRUGO | S_IWUSR); -/* set the major tty number (eg: insmod hso.ko tty_major=245) */ +/* disable network interface (eg: insmod hso.ko disable_net=1) */ MODULE_PARM_DESC(disable_net, "Disable the network interface"); module_param(disable_net, int, S_IRUGO | S_IWUSR); ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <4805C469.7050408-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4805C469.7050408-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-16 11:54 ` Paulius Zaleckas [not found] ` <4805E8E1.3090200-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 11:54 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 440 bytes --] Paulius Zaleckas wrote: > Paulius Zaleckas wrote: >> Reworked MAC address generation from USB serial number >> Now it is very similar to the one done in the cdc_ether.c >> >> Filip: Does the dummy MAC address have to start with 0xFA? >> > > Hopefully fixed last bug in MAC address generation. > Patch against latest Greg unified patch. > Don't use my previous patch. > Incremental patch. Converted ethtool ioctl to ethtool_ops. Paulius [-- Attachment #2: hso_ethtool_ops.patch --] [-- Type: text/x-patch, Size: 2993 bytes --] --- linux-2.6-hso/drivers/net/usb/hso.c 2008-04-16 14:47:53.000000000 +0300 +++ linux-2.6-hso-my/drivers/net/usb/hso.c 2008-04-16 14:40:49.000000000 +0300 @@ -853,61 +853,24 @@ static int hso_net_start_xmit(struct sk_ return result; } -static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +static void hso_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { struct hso_net *odev = netdev_priv(net); - u32 usercmd = 0; char tmp[40]; - switch (cmd) { - case SIOCETHTOOL: - /* net specific */ - if (copy_from_user(&usercmd, rq->ifr_data, sizeof(usercmd))) - return -EFAULT; - - switch (usercmd) { - case ETHTOOL_GDRVINFO: - { - /* get driver info */ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRIVER_VERSION, - ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "usb%d:%d", - odev->parent->usb->bus->busnum, - odev->parent->usb->devnum); - strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "%s %x.%x", driver_name, - ((odev->bcdCDC & 0xff00) >> 8), - (odev->bcdCDC & 0x00ff)); - strncpy(info.fw_version, tmp, ETHTOOL_BUSINFO_LEN); - if (copy_to_user(rq->ifr_data, &info, sizeof(info))) - return -EFAULT; - - return 0; - } - case ETHTOOL_GLINK: - { - /* get link status */ - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = netif_carrier_ok(net); - if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) - return -EFAULT; - - return 0; - } - default: - dev_warn(&net->dev, "Got unsupported ioctl: %x\n", - usercmd); - /* the ethtool user space tool relies on this */ - return -EOPNOTSUPP; - } - default: - return -ENOTTY; /* per ioctl man page */ - } + strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); + strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + usb_make_path(odev->parent->usb, info->bus_info, sizeof info->bus_info); + sprintf(tmp, "%s %x.%x", driver_name, ((odev->bcdCDC & 0xff00) >> 8), + (odev->bcdCDC & 0x00ff)); + strncpy(info->fw_version, tmp, ETHTOOL_BUSINFO_LEN); } +static struct ethtool_ops ops = { + .get_drvinfo = hso_get_drvinfo, + .get_link = ethtool_op_get_link +}; + static struct net_device_stats *hso_net_get_stats(struct net_device *net) { return &((struct hso_net *)netdev_priv(net))->stats; @@ -2281,7 +2244,6 @@ static void hso_net_init(struct net_devi net->open = hso_net_open; net->stop = hso_net_close; net->hard_start_xmit = hso_net_start_xmit; - net->do_ioctl = hso_net_ioctl; net->get_stats = hso_net_get_stats; net->tx_timeout = hso_net_tx_timeout; net->watchdog_timeo = HSO_NET_TX_TIMEOUT; @@ -2289,6 +2251,7 @@ static void hso_net_init(struct net_devi net->flags |= IFF_NOARP; net->mtu = DEFAULT_MTU - 14; net->tx_queue_len = 10; + SET_ETHTOOL_OPS(net, &ops); hso_net->skb_rx_buf = NULL; hso_net->rx_parse_state = WAIT_IP; hso_net->wMaxSegmentSize = DEFAULT_MTU; ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <4805E8E1.3090200-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4805E8E1.3090200-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-16 12:03 ` Oliver Neukum [not found] ` <200804161403.20955.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-16 15:15 ` Paulius Zaleckas 1 sibling, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-16 12:03 UTC (permalink / raw) To: Paulius Zaleckas Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA Am Mittwoch, 16. April 2008 13:54:09 schrieb Paulius Zaleckas: > Incremental patch. Converted ethtool ioctl to ethtool_ops. You were faster. We need to coordinate. Which changes are you doing now? Regards Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804161403.20955.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804161403.20955.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-16 12:12 ` Paulius Zaleckas [not found] ` <4805ED16.3080104-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 12:12 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA Oliver Neukum wrote: > Am Mittwoch, 16. April 2008 13:54:09 schrieb Paulius Zaleckas: > >> Incremental patch. Converted ethtool ioctl to ethtool_ops. > > You were faster. We need to coordinate. Which changes are you doing now? > Actually I am tuning my newly added hso_get_drvinfo... Instead of writing to tmp buffer to write directly to fw_version. Should I post incremental patch or replacement? I always get crash when closing serial terminal application. I will post dump in couple minutes. Paulius -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <4805ED16.3080104-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4805ED16.3080104-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-16 13:43 ` Oliver Neukum [not found] ` <200804161543.23584.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-17 21:33 ` Greg KH 0 siblings, 2 replies; 54+ messages in thread From: Oliver Neukum @ 2008-04-16 13:43 UTC (permalink / raw) To: Paulius Zaleckas Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA Am Mittwoch, 16. April 2008 14:12:06 schrieb Paulius Zaleckas: > I always get crash when closing serial terminal application. I will > post dump in couple minutes. Does this fix it? Regards Oliver Signed-off-by: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org> --- --- linux-2.6.25-hso/drivers/net/usb/hso.c.alt3 2008-04-16 14:01:22.000000000 +0200 +++ linux-2.6.25-hso/drivers/net/usb/hso.c 2008-04-16 15:39:24.000000000 +0200 @@ -156,7 +156,7 @@ struct hso_shared_int { struct usb_device *usb; int use_count; int ref_count; - spinlock_t shared_int_lock; + struct mutex shared_int_lock; }; struct hso_net { @@ -2040,14 +2040,14 @@ static int hso_start_serial_device(struc } } } else { - spin_lock_bh(&serial->shared_int->shared_int_lock); + mutex_lock(&serial->shared_int->shared_int_lock); if (!serial->shared_int->use_count) { result = hso_mux_submit_intr_urb(serial->shared_int, - hso_dev->usb, GFP_ATOMIC); + hso_dev->usb, flags); } serial->shared_int->use_count++; - spin_unlock_bh(&serial->shared_int->shared_int_lock); + mutex_unlock(&serial->shared_int->shared_int_lock); } return result; @@ -2070,7 +2070,7 @@ static int hso_stop_serial_device(struct usb_kill_urb(serial->rx_urb[i]); if (serial->shared_int) { - spin_lock_bh(&serial->shared_int->shared_int_lock); + mutex_lock(&serial->shared_int->shared_int_lock); if (serial->shared_int->use_count && (--serial->shared_int->use_count == 0)) { struct urb *urb; @@ -2079,7 +2079,7 @@ static int hso_stop_serial_device(struct if (urb) usb_kill_urb(urb); } - spin_unlock_bh(&serial->shared_int->shared_int_lock); + mutex_unlock(&serial->shared_int->shared_int_lock); } return 0; @@ -2352,10 +2352,10 @@ static void hso_free_serial_device(struc hso_serial_common_free(serial); if (serial->shared_int) { - spin_lock_bh(&serial->shared_int->shared_int_lock); + mutex_lock(&serial->shared_int->shared_int_lock); if (--serial->shared_int->ref_count == 0) hso_free_shared_int(serial->shared_int); - spin_unlock_bh(&serial->shared_int->shared_int_lock); + mutex_unlock(&serial->shared_int->shared_int_lock); } kfree(serial); hso_free_device(hso_dev); @@ -2468,9 +2468,9 @@ struct hso_device *hso_create_mux_serial serial->write_data = hso_mux_serial_write_data; serial->shared_int = mux; - spin_lock_bh(&serial->shared_int->shared_int_lock); + mutex_lock(&serial->shared_int->shared_int_lock); serial->shared_int->ref_count++; - spin_unlock_bh(&serial->shared_int->shared_int_lock); + mutex_unlock(&serial->shared_int->shared_int_lock); /* and record this serial */ set_serial_by_index(serial->minor, serial); @@ -2535,7 +2535,7 @@ struct hso_shared_int *hso_create_shared goto exit; } - spin_lock_init(&mux->shared_int_lock); + mutex_init(&mux->shared_int_lock); return mux; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804161543.23584.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804161543.23584.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-16 13:55 ` Paulius Zaleckas 2008-04-17 14:32 ` Oliver Neukum 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 13:55 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA Oliver Neukum wrote: > Am Mittwoch, 16. April 2008 14:12:06 schrieb Paulius Zaleckas: >> I always get crash when closing serial terminal application. I will >> post dump in couple minutes. > > Does this fix it? > Yes, it does! Thank you. Paulius -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-16 13:55 ` Paulius Zaleckas @ 2008-04-17 14:32 ` Oliver Neukum [not found] ` <200804171632.12972.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-17 14:32 UTC (permalink / raw) To: Paulius Zaleckas; +Cc: netdev, linux-usb Am Mittwoch, 16. April 2008 15:55:02 schrieb Paulius Zaleckas: > Oliver Neukum wrote: > > Am Mittwoch, 16. April 2008 14:12:06 schrieb Paulius Zaleckas: > >> I always get crash when closing serial terminal application. I will > >> post dump in couple minutes. > > > > Does this fix it? > > > > Yes, it does! Thank you. Hi, can you test this one as well. The method used to determine whether a device is asleep is racy. This introduces a private test. Regards Oliver --- --- linux-2.6.25hso/drivers/net/usb/hso.c.alt2 2008-04-17 14:15:32.000000000 +0200 +++ linux-2.6.25hso/drivers/net/usb/hso.c 2008-04-17 16:11:50.000000000 +0200 @@ -242,6 +242,7 @@ struct hso_device { u8 is_active; u8 suspend_disabled; u8 usb_gone; + u8 sleeping; struct work_struct async_get_intf; struct work_struct async_put_intf; @@ -252,6 +253,7 @@ struct hso_device { struct device *dev; struct kref ref; struct mutex mutex; + spinlock_t lock; /* TODO: Not sure about the ones below */ struct proc_dir_entry *ourproc; @@ -2184,6 +2186,7 @@ static struct hso_device *hso_create_dev hso_dev->interface = intf; kref_init(&hso_dev->ref); mutex_init(&hso_dev->mutex); + spin_lock_init(&hso_dev->lock); INIT_WORK(&hso_dev->async_get_intf, async_get_intf); INIT_WORK(&hso_dev->async_put_intf, async_put_intf); @@ -2757,12 +2760,16 @@ static void async_put_intf(struct work_s static int hso_get_activity(struct hso_device *hso_dev) { - if (hso_dev->usb->state == USB_STATE_SUSPENDED) { + unsigned long flags; + + spin_lock_irqsave(&hso_dev->lock, flags); + if (hso_dev->sleeping) { if (!hso_dev->is_active) { hso_dev->is_active = 1; schedule_work(&hso_dev->async_get_intf); } } + spin_unlock_irqrestore(&hso_dev->lock, flags); if (hso_dev->usb->state != USB_STATE_CONFIGURED) return -EAGAIN; @@ -2774,22 +2781,32 @@ static int hso_get_activity(struct hso_d static int hso_put_activity(struct hso_device *hso_dev) { - if (hso_dev->usb->state != USB_STATE_SUSPENDED) { + unsigned long flags; + + spin_lock_irqsave(&hso_dev->lock, flags); + if (hso_dev->sleeping) { if (hso_dev->is_active) { hso_dev->is_active = 0; schedule_work(&hso_dev->async_put_intf); + spin_unlock_irqrestore(&hso_dev->lock, flags); return -EAGAIN; } } hso_dev->is_active = 0; + spin_unlock_irqrestore(&hso_dev->lock, flags); return 0; } /* called by kernel when we need to suspend device */ static int hso_suspend(struct usb_interface *iface, pm_message_t message) { + struct hso_device *hso_dev = usb_get_intfdata(iface); int i, result = 0; + spin_lock_irq(&hso_dev->lock); + hso_dev->sleeping = 1; + spin_unlock_irq(&hso_dev->lock); + /* Stop all serial ports */ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == iface)) { @@ -2816,9 +2833,14 @@ out: /* called by kernel when we need to resume device */ static int hso_resume(struct usb_interface *iface) { + struct hso_device *hso_dev = usb_get_intfdata(iface); int i, result = 0; struct hso_net *hso_net; + spin_lock_irq(&hso_dev->lock); + hso_dev->sleeping = 0; + spin_unlock_irq(&hso_dev->lock); + /* Start all serial ports */ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == iface)) { ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804171632.12972.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804171632.12972.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-17 21:47 ` Paulius Zaleckas [not found] ` <4807C56F.5060804-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 2008-04-18 15:18 ` Paulius Zaleckas 1 sibling, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-17 21:47 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA Oliver Neukum wrote: > can you test this one as well. The method used to determine whether > a device is asleep is racy. This introduces a private test. Stupid question :) How to test it? How can I force it to suspend? I have never used any suspend on my PC :) Paulius -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <4807C56F.5060804-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* RE: Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4807C56F.5060804-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-17 22:31 ` Chetty, Jay 2008-04-18 6:51 ` Oliver Neukum 1 sibling, 0 replies; 54+ messages in thread From: Chetty, Jay @ 2008-04-17 22:31 UTC (permalink / raw) To: Paulius Zaleckas, linux-usb-u79uwXL29TY76Z2rM5mHXA Cc: netdev-u79uwXL29TY76Z2rM5mHXA read Documentation/usb/power-management.txt -----Original Message----- From: linux-usb-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-usb-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Paulius Zaleckas Sent: Thursday, April 17, 2008 2:47 PM To: linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Subject: Re: [RFC] Patch to option HSO driver to the kernel Oliver Neukum wrote: > can you test this one as well. The method used to determine whether > a device is asleep is racy. This introduces a private test. Stupid question :) How to test it? How can I force it to suspend? I have never used any suspend on my PC :) Paulius -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4807C56F.5060804-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 2008-04-17 22:31 ` Chetty, Jay @ 2008-04-18 6:51 ` Oliver Neukum 1 sibling, 0 replies; 54+ messages in thread From: Oliver Neukum @ 2008-04-18 6:51 UTC (permalink / raw) To: Paulius Zaleckas Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA Am Donnerstag, 17. April 2008 23:47:27 schrieb Paulius Zaleckas: > Oliver Neukum wrote: > > can you test this one as well. The method used to determine whether > > a device is asleep is racy. This introduces a private test. > > Stupid question :) How to test it? How can I force it to suspend? > I have never used any suspend on my PC :) This is runtime power management. It can save energy on any computer. You compile your kernel with CONFIG_USB_SUSPEND and to see something in syslog with CONFIG_USB_DEBUG. Plug in the device, locate its sysfs directory, cd into it and do echo "auto" >power/level After a few seconds of idleness the device and the hub it's connected to (if it's the only device at that hub) should suspend. You'll get a message about it in the kernel log. Regards Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804171632.12972.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 2008-04-17 21:47 ` Paulius Zaleckas @ 2008-04-18 15:18 ` Paulius Zaleckas 2008-04-21 11:45 ` Oliver Neukum 1 sibling, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-18 15:18 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA Oliver Neukum wrote: > > Hi, > > can you test this one as well. The method used to determine whether > a device is asleep is racy. This introduces a private test. > > Regards > Oliver By setting level "auto" it didn't suspend... When I tried to set "suspend" to the level I got crash: hub 1-1.3:1.0: suspend error -16 Unable to handle kernel NULL pointer dereference at 000000000000004a RIP: [<ffffffff811ce082>] usb_kill_urb+0x28/0xf5 PGD 20c36067 PUD 2228e067 PMD 0 Oops: 0000 [1] SMP CPU 0 Modules linked in: hso(U) rfkill i915 drm autofs4 fuse sunrpc loop dm_multipath ipv6 snd_hda_intel parport_pc pcspkr parport e100 snd_seq_dummy r8169 mii i2c_i801 snd_seq_oss i2c_core snd_seq_midi_event snd_seq iTCO_wdt iTCO_vendor_support ftdi_sio snd_seq_device usbserial usblp snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc snd_hwdep snd soundcore button sr_mod sg cdrom dm_snapshot dm_zero dm_mirror dm_mod pata_acpi ata_generic ata_piix libata sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd Pid: 2920, comm: bash Not tainted 2.6.24.4-64.fc8debug #1 RIP: 0010:[<ffffffff811ce082>] [<ffffffff811ce082>] usb_kill_urb+0x28/0xf5 RSP: 0018:ffff81001fd8dd88 EFLAGS: 00010202 RAX: 0000000000000000 RBX: 0000000000000002 RCX: ffffffff88323780 RDX: ffff81001fd8dfd8 RSI: 000000000000021e RDI: ffffffff813868d6 RBP: ffff810032c37b78 R08: 000000011fd88848 R09: 0000000000000000 R10: ffff81001fd8dd68 R11: 0000000000000028 R12: 0000000000000002 R13: ffff810037f38000 R14: 0000000000000002 R15: 0000000000000001 FS: 00002aaaaaac7f50(0000) GS:ffffffff81407000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 000000000000004a CR3: 00000000124f4000 CR4: 00000000000006a0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process bash (pid: 2920, threadinfo ffff81001fd8c000, task ffff81001fd88000) Stack: 0000000000000246 ffff81003319d248 ffff81002d486e98 0000000000000246 ffff81003319d250 ffff81003319d248 ffff81003319d250 ffff810032c37b68 ffff810032c37b78 ffffffff8831ce54 0000000000000002 ffff81003d0f99b0 Call Trace: [<ffffffff8831ce54>] :hso:hso_stop_serial_device+0x4b/0x9b [<ffffffff8831cf13>] :hso:hso_suspend+0x26/0x67 [<ffffffff811d0887>] usb_suspend_both+0xd7/0x27d [<ffffffff811d0a60>] usb_external_suspend_device+0x33/0x45 [<ffffffff811d36b4>] set_level+0x125/0x189 [<ffffffff810ee242>] sysfs_write_file+0xd7/0x115 [<ffffffff810aa62f>] vfs_write+0xc6/0x16f [<ffffffff810aabec>] sys_write+0x45/0x6e [<ffffffff8100c053>] tracesys+0xd5/0xda Code: 48 83 7b 48 00 0f 84 bb 00 00 00 48 83 7b 50 00 0f 84 b0 00 RIP [<ffffffff811ce082>] usb_kill_urb+0x28/0xf5 RSP <ffff81001fd8dd88> CR2: 000000000000004a ---[ end trace e19a70acc4bb0aa4 ]--- -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-18 15:18 ` Paulius Zaleckas @ 2008-04-21 11:45 ` Oliver Neukum 2008-04-21 12:38 ` Paulius Zaleckas 0 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-21 11:45 UTC (permalink / raw) To: Paulius Zaleckas; +Cc: netdev, linux-usb Am Freitag, 18. April 2008 17:18:29 schrieb Paulius Zaleckas: > > Oliver Neukum wrote: > > > > Hi, > > > > can you test this one as well. The method used to determine whether > > a device is asleep is racy. This introduces a private test. > > > > Regards > > Oliver > > By setting level "auto" it didn't suspend... > When I tried to set "suspend" to the level I got crash: Does this fix it? Regards Oliver --- --- linux-2.6.25hso/drivers/net/usb/hso.c.alt3 2008-04-21 13:24:47.000000000 +0200 +++ linux-2.6.25hso/drivers/net/usb/hso.c 2008-04-21 13:25:18.000000000 +0200 @@ -2052,7 +2052,7 @@ } if (serial->tx_urb) - usb_kill_urb(serial->rx_urb[i]); + usb_kill_urb(serial->tx_urb); if (serial->shared_int) { mutex_lock(&serial->shared_int->shared_int_lock); ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-21 11:45 ` Oliver Neukum @ 2008-04-21 12:38 ` Paulius Zaleckas [not found] ` <480C8AD8.9050609-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-21 12:38 UTC (permalink / raw) To: netdev; +Cc: linux-usb Oliver Neukum wrote: > Am Freitag, 18. April 2008 17:18:29 schrieb Paulius Zaleckas: >> Oliver Neukum wrote: >>> Hi, >>> >>> can you test this one as well. The method used to determine whether >>> a device is asleep is racy. This introduces a private test. >>> >>> Regards >>> Oliver >> By setting level "auto" it didn't suspend... >> When I tried to set "suspend" to the level I got crash: > > Does this fix it? Looks like it fixed this crash. I don't see anything on dmesg and /var/log/messages when I do echo "suspend" > power/level, but that is IMO another story. Paulius ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <480C8AD8.9050609-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <480C8AD8.9050609-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> @ 2008-04-21 12:50 ` Oliver Neukum [not found] ` <200804211450.27093.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-21 12:50 UTC (permalink / raw) To: Paulius Zaleckas Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA Am Montag, 21. April 2008 14:38:48 schrieb Paulius Zaleckas: > Oliver Neukum wrote: > > Am Freitag, 18. April 2008 17:18:29 schrieb Paulius Zaleckas: > >> Oliver Neukum wrote: > >>> Hi, > >>> > >>> can you test this one as well. The method used to determine whether > >>> a device is asleep is racy. This introduces a private test. > >>> > >>> Regards > >>> Oliver > >> By setting level "auto" it didn't suspend... > >> When I tried to set "suspend" to the level I got crash: > > > > Does this fix it? > > Looks like it fixed this crash. > I don't see anything on dmesg and /var/log/messages when I do > echo "suspend" > power/level, but that is IMO another story. Did you compile with CONFIG_USB_DEBUG? Do you load usbcore from initrd? In that case you need to rebuild your initrd. Regards Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <200804211450.27093.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <200804211450.27093.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-21 13:00 ` Paulius Zaleckas 0 siblings, 0 replies; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-21 13:00 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA Oliver Neukum wrote: > Am Montag, 21. April 2008 14:38:48 schrieb Paulius Zaleckas: >> Oliver Neukum wrote: >>> Am Freitag, 18. April 2008 17:18:29 schrieb Paulius Zaleckas: >>>> Oliver Neukum wrote: >>>>> Hi, >>>>> >>>>> can you test this one as well. The method used to determine whether >>>>> a device is asleep is racy. This introduces a private test. >>>>> >>>>> Regards >>>>> Oliver >>>> By setting level "auto" it didn't suspend... >>>> When I tried to set "suspend" to the level I got crash: >>> Does this fix it? >> Looks like it fixed this crash. >> I don't see anything on dmesg and /var/log/messages when I do >> echo "suspend" > power/level, but that is IMO another story. > > Did you compile with CONFIG_USB_DEBUG? Do you load usbcore > from initrd? In that case you need to rebuild your initrd. Actually I am using 2.6.24.4-64.fc8debug kernel. I failed to start my 2.6.25 compiled kernel, because of some mkinitrd incompatibilities with LVM (I hate that day when I installed Fedora on LVM partition :)) According to fedora config CONFIG_USB_DEBUG is enabled. Couldn't find usbcore on initrd image... Paulius -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-16 13:43 ` Oliver Neukum [not found] ` <200804161543.23584.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org> @ 2008-04-17 21:33 ` Greg KH 1 sibling, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-17 21:33 UTC (permalink / raw) To: Oliver Neukum; +Cc: Paulius Zaleckas, netdev, linux-usb On Wed, Apr 16, 2008 at 03:43:21PM +0200, Oliver Neukum wrote: > Am Mittwoch, 16. April 2008 14:12:06 schrieb Paulius Zaleckas: > > I always get crash when closing serial terminal application. I will > > post dump in couple minutes. > > Does this fix it? > > Regards > Oliver > > Signed-off-by: Oliver Neukum <oneukum@suse.de> Applied, thanks. greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <4805E8E1.3090200-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org> 2008-04-16 12:03 ` Oliver Neukum @ 2008-04-16 15:15 ` Paulius Zaleckas 1 sibling, 0 replies; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 15:15 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 628 bytes --] Paulius Zaleckas wrote: > Paulius Zaleckas wrote: >> Paulius Zaleckas wrote: >>> Reworked MAC address generation from USB serial number >>> Now it is very similar to the one done in the cdc_ether.c >>> >>> Filip: Does the dummy MAC address have to start with 0xFA? >>> >> >> Hopefully fixed last bug in MAC address generation. >> Patch against latest Greg unified patch. >> Don't use my previous patch. >> > > Incremental patch. Converted ethtool ioctl to ethtool_ops. > > Paulius > Updated patch with the following change: In function hso_get_drvinfo instead of writing to tmp buffer write directly to fw_version. Paulius [-- Attachment #2: hso_ethtool_ops2.patch --] [-- Type: text/x-patch, Size: 2974 bytes --] --- linux-2.6-hso/drivers/net/usb/hso.c 2008-04-16 14:47:53.000000000 +0300 +++ linux-2.6-hso-my/drivers/net/usb/hso.c 2008-04-16 18:01:11.000000000 +0300 @@ -853,61 +853,22 @@ static int hso_net_start_xmit(struct sk_ return result; } -static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +static void hso_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { struct hso_net *odev = netdev_priv(net); - u32 usercmd = 0; - char tmp[40]; - switch (cmd) { - case SIOCETHTOOL: - /* net specific */ - if (copy_from_user(&usercmd, rq->ifr_data, sizeof(usercmd))) - return -EFAULT; - - switch (usercmd) { - case ETHTOOL_GDRVINFO: - { - /* get driver info */ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRIVER_VERSION, - ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "usb%d:%d", - odev->parent->usb->bus->busnum, - odev->parent->usb->devnum); - strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "%s %x.%x", driver_name, - ((odev->bcdCDC & 0xff00) >> 8), - (odev->bcdCDC & 0x00ff)); - strncpy(info.fw_version, tmp, ETHTOOL_BUSINFO_LEN); - if (copy_to_user(rq->ifr_data, &info, sizeof(info))) - return -EFAULT; - - return 0; - } - case ETHTOOL_GLINK: - { - /* get link status */ - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = netif_carrier_ok(net); - if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) - return -EFAULT; - - return 0; - } - default: - dev_warn(&net->dev, "Got unsupported ioctl: %x\n", - usercmd); - /* the ethtool user space tool relies on this */ - return -EOPNOTSUPP; - } - default: - return -ENOTTY; /* per ioctl man page */ - } + strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); + strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + usb_make_path(odev->parent->usb, info->bus_info, sizeof info->bus_info); + snprintf(info->fw_version, ETHTOOL_BUSINFO_LEN, "%s %x.%x", driver_name, + ((odev->bcdCDC & 0xff00) >> 8), (odev->bcdCDC & 0x00ff)); } +static struct ethtool_ops ops = { + .get_drvinfo = hso_get_drvinfo, + .get_link = ethtool_op_get_link +}; + static struct net_device_stats *hso_net_get_stats(struct net_device *net) { return &((struct hso_net *)netdev_priv(net))->stats; @@ -2281,7 +2242,6 @@ static void hso_net_init(struct net_devi net->open = hso_net_open; net->stop = hso_net_close; net->hard_start_xmit = hso_net_start_xmit; - net->do_ioctl = hso_net_ioctl; net->get_stats = hso_net_get_stats; net->tx_timeout = hso_net_tx_timeout; net->watchdog_timeo = HSO_NET_TX_TIMEOUT; @@ -2289,6 +2249,7 @@ static void hso_net_init(struct net_devi net->flags |= IFF_NOARP; net->mtu = DEFAULT_MTU - 14; net->tx_queue_len = 10; + SET_ETHTOOL_OPS(net, &ops); hso_net->skb_rx_buf = NULL; hso_net->rx_parse_state = WAIT_IP; hso_net->wMaxSegmentSize = DEFAULT_MTU; ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> ` (5 preceding siblings ...) 2008-04-15 22:49 ` Paulius Zaleckas @ 2008-04-16 13:11 ` Paulius Zaleckas 6 siblings, 0 replies; 54+ messages in thread From: Paulius Zaleckas @ 2008-04-16 13:11 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA; +Cc: netdev-u79uwXL29TY76Z2rM5mHXA With the latest patch I always get BUG when closing my serial terminal application. I have two different modems (GTM378 and GTM380) plugged at USB hub. Here is the dump: BUG: sleeping function called from invalid context at drivers/usb/core/urb.c:542 in_atomic():1, irqs_disabled():0 Pid: 30031, comm: gtkterm Not tainted 2.6.24.4-64.fc8 #1 Call Trace: [<ffffffff811b5e40>] usb_kill_urb+0x1a/0xf1 [<ffffffff81268512>] mutex_lock+0x1e/0x2f [<ffffffff810ab2a2>] fasync_helper+0x4a/0xcd [<ffffffff88308f57>] :hso:hso_stop_serial_device+0x85/0x99 [<ffffffff8830971f>] :hso:hso_serial_close+0xea/0x110 [<ffffffff8117bf26>] release_dev+0x212/0x618 [<ffffffff8108ec31>] free_pages_and_swap_cache+0x73/0x8f [<ffffffff8117c33d>] tty_release+0x11/0x1a [<ffffffff810a152c>] __fput+0xc2/0x18f [<ffffffff8109ed3f>] filp_close+0x5d/0x65 [<ffffffff8109ff0f>] sys_close+0x8d/0xca [<ffffffff8100c005>] tracesys+0xd5/0xda BUG: scheduling while atomic: gtkterm/30031/0x10000100 Pid: 30031, comm: gtkterm Not tainted 2.6.24.4-64.fc8 #1 Call Trace: [<ffffffff812677a2>] schedule+0x91/0x706 [<ffffffff81032f45>] __cond_resched+0x2d/0x55 [<ffffffff81267f0f>] cond_resched+0x2e/0x39 [<ffffffff811b5e45>] usb_kill_urb+0x1f/0xf1 [<ffffffff81268512>] mutex_lock+0x1e/0x2f [<ffffffff810ab2a2>] fasync_helper+0x4a/0xcd [<ffffffff88308f57>] :hso:hso_stop_serial_device+0x85/0x99 [<ffffffff8830971f>] :hso:hso_serial_close+0xea/0x110 [<ffffffff8117bf26>] release_dev+0x212/0x618 [<ffffffff8108ec31>] free_pages_and_swap_cache+0x73/0x8f [<ffffffff8117c33d>] tty_release+0x11/0x1a [<ffffffff810a152c>] __fput+0xc2/0x18f [<ffffffff8109ed3f>] filp_close+0x5d/0x65 [<ffffffff8109ff0f>] sys_close+0x8d/0xca [<ffffffff8100c005>] tracesys+0xd5/0xda -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-14 21:32 [RFC] Patch to option HSO driver to the kernel Greg KH [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-15 4:30 ` Oliver Hartkopp [not found] ` <48042F7F.8030608-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org> 2008-04-15 13:55 ` Oliver Neukum 2 siblings, 1 reply; 54+ messages in thread From: Oliver Hartkopp @ 2008-04-15 4:30 UTC (permalink / raw) To: Greg KH; +Cc: linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas, ajb Just some obvious things to reduce the source code hopping for the review and to reduce the source code size. Regards, Oliver > + > +#define DRIVER_VERSION "1.2" > +#define MOD_AUTHOR "Option Wireless" > +#define MOD_DESCRIPTION "USB High Speed Option driver" > +#define MOD_LICENSE "GPL" > + > Please move the MODULE_OWNER(), MODULE_LICENSE(), etc. macros from the end of this file directly to this point. > +/*****************************************************************************/ > +/* Prototypes */ > +/*****************************************************************************/ > +/* Network interface functions */ > +static int hso_net_open(struct net_device *net); > +static int hso_net_close(struct net_device *net); > +static int hso_net_start_xmit(struct sk_buff *skb, struct net_device *net); > +static int hso_net_ioctl(struct net_device *net, struct ifreq *rq, int cmd); > +static struct net_device_stats *hso_net_get_stats(struct net_device *net); > +static void hso_net_tx_timeout(struct net_device *net); > +static void hso_net_set_multicast(struct net_device *net); > +static void read_bulk_callback(struct urb *urb); > +static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, > + unsigned int count, unsigned char is_eop); > +static void write_bulk_callback(struct urb *urb); > +/* 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(struct urb *urb); > +static void put_rxbuf_data(struct urb *urb, struct hso_serial *serial); > +static void hso_std_serial_read_bulk_callback(struct urb *urb); > +static void hso_std_serial_write_bulk_callback(struct urb *urb); > +static void _hso_serial_set_termios(struct tty_struct *tty, > + struct ktermios *old); > +static void hso_kick_transmit(struct hso_serial *serial); > +/* Base driver functions */ > +static int hso_probe(struct usb_interface *interface, > + const struct usb_device_id *id); > +static void hso_disconnect(struct usb_interface *interface); > +/* Helper functions */ > +static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, > + struct usb_device *usb, gfp_t gfp); > +static void hso_net_init(struct net_device *net); > +static void set_ethernet_addr(struct hso_net *odev); > +static struct hso_serial *get_serial_by_index(unsigned index); > +static struct hso_serial *get_serial_by_shared_int_and_type( > + struct hso_shared_int *shared_int, > + int mux); > +static int get_free_serial_index(void); > +static void set_serial_by_index(unsigned index, struct hso_serial *serial); > +static int remove_net_device(struct hso_device *hso_dev); > +static int add_net_device(struct hso_device *hso_dev); > +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); > +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 int hso_suspend(struct usb_interface *iface, pm_message_t message); > +static int hso_resume(struct usb_interface *iface); > +static 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); > + > This is a real forward declaration hell. Please try to reorder the functions in the code to make this as obsolte as possible. > +/*****************************************************************************/ > +/* Helping functions */ > +/*****************************************************************************/ > + > +/* convert a character representing a hex value to a number */ > +static unsigned char hex2dec(unsigned char digit) > +{ > + > + if ((digit >= '0') && (digit <= '9')) > + return (digit - '0'); > + /* Map all characters to 0-15 */ > + if ((digit >= 'a') && (digit <= 'z')) > + return (digit - 'a' + 10) % 16; > + if ((digit >= 'A') && (digit <= 'Z')) > + return (digit - 'A' + 10) % 16; > + return 16; > +} > + > Shouldn't this already be somewhere else in the kernel? > + > +/* module parameters */ > +static int debug; > +static int procfs = 1; > +static int tty_major; > +static int disable_net; > +static int enable_autopm; > + > please move the module_param() stuff and MODULE_PARM_DESC() from the end of the file right to this place. > +static int hso_proc_port_info(char *buf, char **start, off_t offset, int count, > + int *eof, void *data) > +{ > + int len = 0; > + struct hso_device *hso_dev = (struct hso_device *)data; > + char *port_name = NULL; > + > + D1("count: %d", count); > This debug macro looks like it is from the very beginning of the implementation. Remove it. > + > +static void hso_serial_throttle(struct tty_struct *tty) > +{ > + D1(" "); > +} > + > +static void hso_serial_unthrottle(struct tty_struct *tty) > +{ > + D1(" "); > +} > + > +static void hso_serial_break(struct tty_struct *tty, int break_state) > +{ > + D1(" "); > +} > + > +static int hso_serial_read_proc(char *page, char **start, off_t off, int count, > + int *eof, void *data) > +{ > + return 0; > +} > + > ??? > +static void hso_serial_hangup(struct tty_struct *tty) > +{ > + D1("hang up"); > +} > + > > Should all these functions be removed (and therefor not set in hso_serial_ops) ?? > + > +/* Base driver functions */ > + > +/* operations setup of the serial interface */ > +static struct tty_operations hso_serial_ops = { > + .open = hso_serial_open, > + .close = hso_serial_close, > + .write = hso_serial_write, > + .write_room = hso_serial_write_room, > + .ioctl = hso_serial_ioctl, > + .set_termios = hso_serial_set_termios, > + .throttle = hso_serial_throttle, > + .unthrottle = hso_serial_unthrottle, > + .break_ctl = hso_serial_break, > + .chars_in_buffer = hso_serial_chars_in_buffer, > + .read_proc = hso_serial_read_proc, > + .hangup = hso_serial_hangup, > + .tiocmget = hso_serial_tiocmget, > + .tiocmset = hso_serial_tiocmset, > +}; > + > ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <48042F7F.8030608-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <48042F7F.8030608-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org> @ 2008-04-15 16:11 ` Greg KH [not found] ` <20080415161158.GE9704-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 54+ messages in thread From: Greg KH @ 2008-04-15 16:11 UTC (permalink / raw) To: Oliver Hartkopp Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io On Tue, Apr 15, 2008 at 06:30:55AM +0200, Oliver Hartkopp wrote: > Just some obvious things to reduce the source code hopping for the > review and to reduce the source code size. > > Regards, > Oliver > > > + > > +#define DRIVER_VERSION "1.2" > > +#define MOD_AUTHOR "Option Wireless" > > +#define MOD_DESCRIPTION "USB High Speed Option driver" > > +#define MOD_LICENSE "GPL" > > + > > > > Please move the MODULE_OWNER(), MODULE_LICENSE(), etc. macros from the > end of this file directly to this point. Does that really matter? > > +/*****************************************************************************/ > > +/* Prototypes */ > > +/*****************************************************************************/ > > > > This is a real forward declaration hell. Please try to reorder the > functions in the code to make this as obsolte as possible. I've already stripped down a lot of them, will work on the remaining ones, I wanted to get the code out for review first. > > +/*****************************************************************************/ > > +/* Helping functions */ > > +/*****************************************************************************/ > > + > > +/* convert a character representing a hex value to a number */ > > +static unsigned char hex2dec(unsigned char digit) > > +{ > > + > > + if ((digit >= '0') && (digit <= '9')) > > + return (digit - '0'); > > + /* Map all characters to 0-15 */ > > + if ((digit >= 'a') && (digit <= 'z')) > > + return (digit - 'a' + 10) % 16; > > + if ((digit >= 'A') && (digit <= 'Z')) > > + return (digit - 'A' + 10) % 16; > > + return 16; > > +} > > + > > > > Shouldn't this already be somewhere else in the kernel? Do you know where? > > + > > +/* module parameters */ > > +static int debug; > > +static int procfs = 1; > > +static int tty_major; > > +static int disable_net; > > +static int enable_autopm; > > + > > > > please move the module_param() stuff and MODULE_PARM_DESC() from the end > of the file right to this place. Does it really matter? > > +static int hso_proc_port_info(char *buf, char **start, off_t offset, int count, > > + int *eof, void *data) > > +{ > > + int len = 0; > > + struct hso_device *hso_dev = (struct hso_device *)data; > > + char *port_name = NULL; > > + > > + D1("count: %d", count); > > > > This debug macro looks like it is from the very beginning of the > implementation. Remove it. Why? > > + > > +static void hso_serial_throttle(struct tty_struct *tty) > > +{ > > + D1(" "); > > +} > > + > > +static void hso_serial_unthrottle(struct tty_struct *tty) > > +{ > > + D1(" "); > > +} > > + > > +static void hso_serial_break(struct tty_struct *tty, int break_state) > > +{ > > + D1(" "); > > +} > > + > > +static int hso_serial_read_proc(char *page, char **start, off_t off, int count, > > + int *eof, void *data) > > +{ > > + return 0; > > +} > > + > > > > ??? Already been pointed out that they can be removed. > > +static void hso_serial_hangup(struct tty_struct *tty) > > +{ > > + D1("hang up"); > > +} > > + > > > > > > Should all these functions be removed (and therefor not set in > hso_serial_ops) ?? Yes. thanks for the review. greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
[parent not found: <20080415161158.GE9704-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>]
* Re: [RFC] Patch to option HSO driver to the kernel [not found] ` <20080415161158.GE9704-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> @ 2008-04-15 17:53 ` Oliver Hartkopp 0 siblings, 0 replies; 54+ messages in thread From: Oliver Hartkopp @ 2008-04-15 17:53 UTC (permalink / raw) To: Greg KH Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA, Alan Cox, Filip Aben, Paulius Zaleckas, ajb-5+cxppFmGx6/3pe1ocb+s/XRex20P6io Greg KH wrote: > On Tue, Apr 15, 2008 at 06:30:55AM +0200, Oliver Hartkopp wrote: > >> Just some obvious things to reduce the source code hopping for the >> review and to reduce the source code size. >> >> Regards, >> Oliver >> >> >>> + >>> +#define DRIVER_VERSION "1.2" >>> +#define MOD_AUTHOR "Option Wireless" >>> +#define MOD_DESCRIPTION "USB High Speed Option driver" >>> +#define MOD_LICENSE "GPL" >>> + >>> >>> >> Please move the MODULE_OWNER(), MODULE_LICENSE(), etc. macros from the >> end of this file directly to this point. >> > > Does that really matter? > > Not for the compiler :) But when you are able to put things together that helps to understand the code much better (at least for me). I'm not a wizard so when it helps me to find code belonging together it probably helps other people also. >>> +/*****************************************************************************/ >>> +/* Prototypes */ >>> +/*****************************************************************************/ >>> >>> >> This is a real forward declaration hell. Please try to reorder the >> functions in the code to make this as obsolte as possible. >> > > I've already stripped down a lot of them, will work on the remaining > ones, I wanted to get the code out for review first. > > Ok. >>> +/*****************************************************************************/ >>> +/* Helping functions */ >>> +/*****************************************************************************/ >>> + >>> +/* convert a character representing a hex value to a number */ >>> +static unsigned char hex2dec(unsigned char digit) >>> +{ >>> + >>> + if ((digit >= '0') && (digit <= '9')) >>> + return (digit - '0'); >>> + /* Map all characters to 0-15 */ >>> + if ((digit >= 'a') && (digit <= 'z')) >>> + return (digit - 'a' + 10) % 16; >>> + if ((digit >= 'A') && (digit <= 'Z')) >>> + return (digit - 'A' + 10) % 16; >>> + return 16; >>> +} >>> + >>> >>> >> Shouldn't this already be somewhere else in the kernel? >> > > Do you know where? > > Hm - i found one by grep after 'hex2' in drivers/net/cxgb3/t3_hw.c : static unsigned int hex2int(unsigned char c) { return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; } But nothing in lib/* ... >>> + >>> +/* module parameters */ >>> +static int debug; >>> +static int procfs = 1; >>> +static int tty_major; >>> +static int disable_net; >>> +static int enable_autopm; >>> + >>> >>> >> please move the module_param() stuff and MODULE_PARM_DESC() from the end >> of the file right to this place. >> > > Does it really matter? > > Same as stated above. If you would put the MODULE_PARM_DESC() right here the formerly defined variables are documented in one go without any /* comments */ and you just know the meaning for the rest of the review. >>> +static int hso_proc_port_info(char *buf, char **start, off_t offset, int count, >>> + int *eof, void *data) >>> +{ >>> + int len = 0; >>> + struct hso_device *hso_dev = (struct hso_device *)data; >>> + char *port_name = NULL; >>> + >>> + D1("count: %d", count); >>> >>> >> This debug macro looks like it is from the very beginning of the >> implementation. Remove it. >> > > Why? > Why not? If it is not really needed it should be omitted. In general i'm a friend of debug-code, but when i touches the mainline kernel IMO one should reconsider if all the debug code remains necessary. > >>> + >>> +static int hso_serial_read_proc(char *page, char **start, off_t off, int count, >>> + int *eof, void *data) >>> +{ >>> + return 0; >>> +} >>> + >>> >>> >> ??? >> > > Already been pointed out that they can be removed. > Sorry. Didn't catch that. Regards, Oliver -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-14 21:32 [RFC] Patch to option HSO driver to the kernel Greg KH [not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org> 2008-04-15 4:30 ` Oliver Hartkopp @ 2008-04-15 13:55 ` Oliver Neukum 2008-04-15 16:10 ` Greg KH 2 siblings, 1 reply; 54+ messages in thread From: Oliver Neukum @ 2008-04-15 13:55 UTC (permalink / raw) To: Greg KH; +Cc: linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas, ajb Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > Hi all, > > Here's a patch that I have cleaned up for context only from Option that > is a USB serial / network device all in one. > > I'd like to see this go into 2.6.26, so any review comments by anyone > who wishes to review any portion of this would be greatly apprecited. This patch removes a creative abuse of in_interrupt(). Locking is always done with a mutex now. No need for atomic operations. Signed-off-by: Oliver Neukum <oneukum@suse.de> --- --- linux-2.6.25-rc7-work/drivers/net/usb/hso.c.alt2 2008-04-15 15:21:25.000000000 +0200 +++ linux-2.6.25-rc7-work/drivers/net/usb/hso.c 2008-04-15 15:29:17.000000000 +0200 @@ -2201,21 +2201,13 @@ static int hso_stop_serial_device(struct return -ENODEV; for (i = 0; i < serial->num_rx_urbs; i++) { - if (serial->rx_urb[i] - && (serial->rx_urb[i]->status == -EINPROGRESS)) { - if (in_interrupt()) - usb_unlink_urb(serial->rx_urb[i]); - else + if (serial->rx_urb[i]) usb_kill_urb(serial->rx_urb[i]); - } } - if (serial->tx_urb && (serial->tx_urb->status == -EINPROGRESS)) { - if (in_interrupt()) - usb_unlink_urb(serial->tx_urb); - else - usb_kill_urb(serial->rx_urb[i]); - } + if (serial->tx_urb) + usb_kill_urb(serial->rx_urb[i]); + if (serial->shared_int) { spin_lock_bh(&serial->shared_int->shared_int_lock); if (serial->shared_int->use_count && @@ -2223,12 +2215,8 @@ static int hso_stop_serial_device(struct struct urb *urb; urb = serial->shared_int->shared_intr_urb; - if ((urb) && (urb->status == -EINPROGRESS)) { - if (in_interrupt()) - usb_unlink_urb(urb); - else - usb_kill_urb(urb); - } + if (urb) + usb_kill_urb(urb); } spin_unlock_bh(&serial->shared_int->shared_int_lock); } ^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [RFC] Patch to option HSO driver to the kernel 2008-04-15 13:55 ` Oliver Neukum @ 2008-04-15 16:10 ` Greg KH 0 siblings, 0 replies; 54+ messages in thread From: Greg KH @ 2008-04-15 16:10 UTC (permalink / raw) To: Oliver Neukum Cc: linux-usb, netdev, Alan Cox, Filip Aben, Paulius Zaleckas, ajb On Tue, Apr 15, 2008 at 03:55:06PM +0200, Oliver Neukum wrote: > Am Montag, 14. April 2008 23:32:39 schrieb Greg KH: > > Hi all, > > > > Here's a patch that I have cleaned up for context only from Option that > > is a USB serial / network device all in one. > > > > I'd like to see this go into 2.6.26, so any review comments by anyone > > who wishes to review any portion of this would be greatly apprecited. > > This patch removes a creative abuse of in_interrupt(). Locking > is always done with a mutex now. No need for atomic operations. > > Signed-off-by: Oliver Neukum <oneukum@suse.de> Very nice, thanks for the change, applied. greg k-h ^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2008-04-21 13:00 UTC | newest]
Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-14 21:32 [RFC] Patch to option HSO driver to the kernel Greg KH
[not found] ` <20080414213238.GB28833-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-04-14 21:59 ` Matthew Dharm
2008-04-14 22:42 ` Andrew Bird (Sphere Systems)
2008-04-14 23:03 ` Greg KH
[not found] ` <20080414230309.GA1672-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-04-15 8:01 ` Filip Aben
2008-04-15 15:40 ` Greg KH
2008-04-14 23:20 ` Paulius Zaleckas
2008-04-15 8:10 ` Oliver Neukum
[not found] ` <200804151010.33688.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-15 8:58 ` Paulius Zaleckas
[not found] ` <48046E4A.3060901-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-15 15:39 ` Greg KH
2008-04-15 11:44 ` Oliver Neukum
[not found] ` <200804151344.42085.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-15 16:06 ` Greg KH
2008-04-15 13:06 ` Oliver Neukum
[not found] ` <200804151506.21856.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-15 16:08 ` Greg KH
2008-04-15 16:08 ` Greg KH
2008-04-15 13:25 ` Oliver Neukum
2008-04-15 14:12 ` Filip Aben
2008-04-15 14:14 ` Oliver Neukum
2008-04-15 15:03 ` Filip Aben
2008-04-15 15:34 ` Greg KH
[not found] ` <20080415153408.GB7996-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-04-15 16:24 ` Filip Aben
2008-04-15 17:58 ` Oliver Neukum
2008-04-15 18:46 ` Oliver Neukum
[not found] ` <200804152046.44018.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-15 19:00 ` Greg KH
2008-04-15 19:14 ` Stephen Hemminger
2008-04-15 19:27 ` Greg KH
2008-04-15 20:17 ` Oliver Neukum
[not found] ` <200804152217.25451.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-15 22:18 ` Greg KH
2008-04-17 12:15 ` Oliver Neukum
2008-04-17 21:35 ` Greg KH
2008-04-15 22:49 ` Paulius Zaleckas
[not found] ` <480530E6.8020700-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-16 9:18 ` Paulius Zaleckas
[not found] ` <4805C469.7050408-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-16 11:54 ` Paulius Zaleckas
[not found] ` <4805E8E1.3090200-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-16 12:03 ` Oliver Neukum
[not found] ` <200804161403.20955.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-16 12:12 ` Paulius Zaleckas
[not found] ` <4805ED16.3080104-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-16 13:43 ` Oliver Neukum
[not found] ` <200804161543.23584.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-16 13:55 ` Paulius Zaleckas
2008-04-17 14:32 ` Oliver Neukum
[not found] ` <200804171632.12972.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-17 21:47 ` Paulius Zaleckas
[not found] ` <4807C56F.5060804-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-17 22:31 ` Chetty, Jay
2008-04-18 6:51 ` Oliver Neukum
2008-04-18 15:18 ` Paulius Zaleckas
2008-04-21 11:45 ` Oliver Neukum
2008-04-21 12:38 ` Paulius Zaleckas
[not found] ` <480C8AD8.9050609-Ft0m5Q12RQ9xBelEqimL3w@public.gmane.org>
2008-04-21 12:50 ` Oliver Neukum
[not found] ` <200804211450.27093.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
2008-04-21 13:00 ` Paulius Zaleckas
2008-04-17 21:33 ` Greg KH
2008-04-16 15:15 ` Paulius Zaleckas
2008-04-16 13:11 ` Paulius Zaleckas
2008-04-15 4:30 ` Oliver Hartkopp
[not found] ` <48042F7F.8030608-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
2008-04-15 16:11 ` Greg KH
[not found] ` <20080415161158.GE9704-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-04-15 17:53 ` Oliver Hartkopp
2008-04-15 13:55 ` Oliver Neukum
2008-04-15 16:10 ` Greg KH
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).