aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/rmnet_usb_ctrl.c198
-rw-r--r--drivers/net/usb/rmnet_usb_ctrl.h11
-rw-r--r--drivers/net/usb/rmnet_usb_data.c109
-rw-r--r--drivers/net/usb/usbnet.c107
4 files changed, 376 insertions, 49 deletions
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index ebd58ab..83638a7 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -12,13 +12,20 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/termios.h>
+#include <linux/poll.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
#include "rmnet_usb_ctrl.h"
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
+#endif
+
#define DEVICE_NAME "hsicctl"
#define NUM_CTRL_CHANNELS 4
#define DEFAULT_READ_URB_LENGTH 0x1000
@@ -81,7 +88,7 @@ enum {
do { \
if (ctl_msg_dbg_mask & MSM_USB_CTL_DUMP_BUFFER) \
print_hex_dump(KERN_INFO, prestr, DUMP_PREFIX_NONE, \
- 16, 1, buf, cnt, false); \
+ 16, 1, buf, cnt > 16 ? 16 : cnt, false); \
} while (0)
#define DBG(x...) \
@@ -110,7 +117,7 @@ static int is_dev_connected(struct rmnet_ctrl_dev *dev)
{
if (dev) {
mutex_lock(&dev->dev_lock);
- if (!dev->intf) {
+ if (!dev->is_connected) {
mutex_unlock(&dev->dev_lock);
return 0;
}
@@ -126,11 +133,17 @@ static void notification_available_cb(struct urb *urb)
struct usb_cdc_notification *ctrl;
struct usb_device *udev;
struct rmnet_ctrl_dev *dev = urb->context;
+ unsigned int iface_num;
+
+ if (!dev->intf)
+ return;
udev = interface_to_usbdev(dev->intf);
+ iface_num = dev->intf->cur_altsetting->desc.bInterfaceNumber;
switch (urb->status) {
case 0:
+ pr_info("[NACB:%d]<\n", iface_num);
/*success*/
break;
@@ -166,12 +179,15 @@ static void notification_available_cb(struct urb *urb)
DEFAULT_READ_URB_LENGTH,
resp_avail_cb, dev);
+ usb_mark_last_busy(udev);
status = usb_submit_urb(dev->rcvurb, GFP_ATOMIC);
if (status) {
dev_err(dev->devicep,
"%s: Error submitting Read URB %d\n", __func__, status);
goto resubmit_int_urb;
- }
+ } else
+ pr_info("[NRA:%d]>\n", iface_num);
+ usb_mark_last_busy(udev);
if (!dev->resp_available) {
dev->resp_available = true;
@@ -185,6 +201,7 @@ static void notification_available_cb(struct urb *urb)
}
resubmit_int_urb:
+ usb_mark_last_busy(udev);
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
@@ -201,18 +218,24 @@ static void resp_avail_cb(struct urb *urb)
void *cpkt;
int status = 0;
size_t cpkt_size = 0;
+ unsigned int iface_num;
udev = interface_to_usbdev(dev->intf);
+ iface_num = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ usb_mark_last_busy(udev);
switch (urb->status) {
+ case -ENOENT:
+ /* process rx, but do not re-submit urb for rx */
case 0:
/*success*/
dev->get_encap_resp_cnt++;
+ pr_info("[RACB:%d]<\n", iface_num);
break;
/*do not resubmit*/
case -ESHUTDOWN:
- case -ENOENT:
case -ECONNRESET:
case -EPROTO:
return;
@@ -231,6 +254,12 @@ static void resp_avail_cb(struct urb *urb)
cpkt = urb->transfer_buffer;
cpkt_size = urb->actual_length;
+ if (!cpkt_size) {
+ dev->zlp_cnt++;
+ dev_dbg(dev->devicep, "%s: zero length pkt received\n",
+ __func__);
+ goto resubmit_int_urb;
+ }
list_elem = kmalloc(sizeof(struct ctrl_pkt_list_elem), GFP_ATOMIC);
if (!list_elem) {
@@ -251,22 +280,35 @@ static void resp_avail_cb(struct urb *urb)
spin_unlock(&dev->rx_lock);
wake_up(&dev->read_wait_queue);
+ wake_lock_timeout(&dev->ctrl_wake, msecs_to_jiffies(500));
+
+ if (urb->status == -ENOENT)
+ return;
resubmit_int_urb:
/*re-submit int urb to check response available*/
+ usb_mark_last_busy(udev);
status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
if (status)
dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
__func__, status);
+ pr_info("[CHKRA:%d]>\n", iface_num);
+ usb_mark_last_busy(udev);
}
static int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *dev)
{
int retval = 0;
+ unsigned int iface_num;
+ struct usb_device *udev;
+ iface_num = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ udev = interface_to_usbdev(dev->intf);
+ usb_mark_last_busy(udev);
retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
if (retval < 0)
dev_err(dev->devicep, "%s Intr submit %d\n", __func__, retval);
+ pr_info("[CHKRA:%d]>\n", iface_num);
return retval;
}
@@ -287,6 +329,17 @@ int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *dev)
return 0;
}
+void rmnet_usb_ctrl_stop_all(void)
+{
+ int id;
+
+ for (id = 0; id < NUM_CTRL_CHANNELS; id++) {
+ if (is_dev_connected(ctrl_dev[id]))
+ rmnet_usb_ctrl_stop_rx(ctrl_dev[id]);
+ }
+}
+EXPORT_SYMBOL(rmnet_usb_ctrl_stop_all);
+
int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *dev)
{
int status = 0;
@@ -357,11 +410,18 @@ static void ctrl_write_callback(struct urb *urb)
pr_debug_ratelimited("Write status/size %d/%d\n",
urb->status, urb->actual_length);
}
-
+#if 0
+ dev->tx_ctrl_in_req_cnt--;
+ if (dev->tx_ctrl_in_req_cnt < 0) {
+ pr_err("unbalanced %s\n", __func__);
+ dev->tx_ctrl_in_req_cnt = 0;
+ }
+#endif
kfree(urb->setup_packet);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
- usb_autopm_put_interface_async(dev->intf);
+ if (dev->intf)
+ usb_autopm_put_interface_async(dev->intf);
}
static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
@@ -371,11 +431,26 @@ static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
struct urb *sndurb;
struct usb_ctrlrequest *out_ctlreq;
struct usb_device *udev;
+ int spin = 50;
if (!is_dev_connected(dev))
return -ENETRESET;
+ /* move it to mdm _hsic pm .c, check return code */
+ while (lpa_handling && spin--) {
+ pr_info("%s: lpa wake wait loop\n", __func__);
+ msleep(20);
+ }
+
+ if (lpa_handling) {
+ pr_err("%s: in lpa wakeup, return EAGAIN\n", __func__);
+ return -EAGAIN;
+ }
+
+ DUMP_BUFFER("Write: ", size, buf);
+
udev = interface_to_usbdev(dev->intf);
+ usb_mark_last_busy(udev);
sndurb = usb_alloc_urb(0, GFP_KERNEL);
if (!sndurb) {
@@ -385,6 +460,7 @@ static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_KERNEL);
if (!out_ctlreq) {
+ kfree(buf);
usb_free_urb(sndurb);
dev_err(dev->devicep, "Error allocating setup packet buffer\n");
return -ENOMEM;
@@ -412,7 +488,7 @@ static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
* Revisit: if (result == -EPERM)
* rmnet_usb_suspend(dev->intf, PMSG_SUSPEND);
*/
-
+ kfree(buf);
usb_free_urb(sndurb);
kfree(out_ctlreq);
return result;
@@ -420,6 +496,7 @@ static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
usb_anchor_urb(sndurb, &dev->tx_submitted);
dev->snd_encap_cmd_cnt++;
+ usb_mark_last_busy(udev);
result = usb_submit_urb(sndurb, GFP_KERNEL);
if (result < 0) {
dev_err(dev->devicep, "%s: Submit URB error %d\n",
@@ -427,11 +504,17 @@ static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
dev->snd_encap_cmd_cnt--;
usb_autopm_put_interface(dev->intf);
usb_unanchor_urb(sndurb);
+ kfree(buf);
usb_free_urb(sndurb);
kfree(out_ctlreq);
return result;
}
+#if 0
+ /* assume interface is in stuck, when stacked tx over 10 request */
+ if (dev->tx_ctrl_in_req_cnt++ > 10)
+ mdm_force_fatal();
+#endif
return size;
}
@@ -447,19 +530,11 @@ static int rmnet_ctl_open(struct inode *inode, struct file *file)
if (dev->is_opened)
goto already_opened;
-ctrl_open:
- if (!is_dev_connected(dev)) {
- dev_dbg(dev->devicep, "%s: Device not connected\n",
- __func__);
- return -ENODEV;
- }
-
/*block open to get first response available from mdm*/
if (dev->mdm_wait_timeout && !dev->resp_available) {
retval = wait_event_interruptible_timeout(
dev->open_wait_queue,
- dev->resp_available ||
- !is_dev_connected(dev),
+ dev->resp_available,
msecs_to_jiffies(dev->mdm_wait_timeout *
1000));
if (retval == 0) {
@@ -471,8 +546,6 @@ ctrl_open:
__func__, dev->name);
return retval;
}
-
- goto ctrl_open;
}
if (!dev->resp_available) {
@@ -531,6 +604,28 @@ static int rmnet_ctl_release(struct inode *inode, struct file *file)
return 0;
}
+static unsigned int rmnet_ctl_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct rmnet_ctrl_dev *dev;
+
+ dev = file->private_data;
+ if (!dev)
+ return POLLERR;
+
+ poll_wait(file, &dev->read_wait_queue, wait);
+ if (!is_dev_connected(dev)) {
+ dev_dbg(dev->devicep, "%s: Device not connected\n",
+ __func__);
+ return POLLERR;
+ }
+
+ if (!list_empty(&dev->rx_list))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
static ssize_t rmnet_ctl_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
@@ -562,6 +657,15 @@ ctrl_read:
if (retval < 0)
return retval;
+ /* check device connected, because wait event returns 0
+ * at disconnect, also
+ */
+ if (!is_dev_connected(dev)) {
+ dev_dbg(dev->devicep, "%s: Device not connected\n",
+ __func__);
+ return -ENETRESET;
+ }
+
goto ctrl_read;
}
@@ -611,6 +715,14 @@ static ssize_t rmnet_ctl_write(struct file *file, const char __user * buf,
if (!is_dev_connected(dev))
return -ENETRESET;
+ if (!dev->is_opened)
+ return size;
+
+ if (dev->tx_block) {
+ pr_info("%s: tx blocked by reset, just return\n", __func__);
+ return size;
+ }
+
DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
wbuf = kmalloc(size , GFP_KERNEL);
@@ -625,7 +737,6 @@ static ssize_t rmnet_ctl_write(struct file *file, const char __user * buf,
kfree(wbuf);
return status;
}
- DUMP_BUFFER("Write: ", size, buf);
status = rmnet_usb_ctrl_write(dev, wbuf, size);
if (status == size)
@@ -724,8 +835,23 @@ static const struct file_operations ctrldev_fops = {
.unlocked_ioctl = rmnet_ctrl_ioctl,
.open = rmnet_ctl_open,
.release = rmnet_ctl_release,
+ .poll = rmnet_ctl_poll,
};
+static int rmnet_ctrl_reset_notifier(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct rmnet_ctrl_dev *dev = container_of(this, struct rmnet_ctrl_dev,
+ reset_notifier_block);
+
+ pr_info("%s\n", __func__);
+
+ dev->tx_block = true;
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ return NOTIFY_DONE;
+}
+
int rmnet_usb_ctrl_probe(struct usb_interface *intf,
struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
{
@@ -760,11 +886,20 @@ int rmnet_usb_ctrl_probe(struct usb_interface *intf,
dev->resp_avail_cnt = 0;
dev->tx_ctrl_err_cnt = 0;
dev->set_ctrl_line_state_cnt = 0;
-
- ret = rmnet_usb_ctrl_write_cmd(dev);
+ dev->tx_ctrl_in_req_cnt = 0;
+ dev->tx_block = false;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
+ dev->cbits_tomdm,
+ dev->intf->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
if (ret < 0)
return ret;
+ dev->set_ctrl_line_state_cnt++;
+
dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->inturb) {
dev_err(dev->devicep, "Error allocating int urb\n");
@@ -799,7 +934,18 @@ int rmnet_usb_ctrl_probe(struct usb_interface *intf,
dev->intbuf, wMaxPacketSize,
notification_available_cb, dev, interval);
- return rmnet_usb_ctrl_start_rx(dev);
+ usb_mark_last_busy(udev);
+ ret = rmnet_usb_ctrl_start_rx(dev);
+ if (!ret)
+ dev->is_connected = true;
+
+ ctl_msg_dbg_mask = MSM_USB_CTL_DUMP_BUFFER;
+
+ dev->reset_notifier_block.notifier_call = rmnet_ctrl_reset_notifier;
+ blocking_notifier_chain_register(&mdm_reset_notifier_list,
+ &dev->reset_notifier_block);
+
+ return ret;
}
void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
@@ -812,6 +958,7 @@ void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
dev->cbits_tolocal = ~ACM_CTRL_CD;
dev->cbits_tomdm = ~ACM_CTRL_DTR;
+ dev->is_connected = false;
dev->intf = NULL;
mutex_unlock(&dev->dev_lock);
@@ -827,7 +974,7 @@ void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_BUF_SIZE 4096
static ssize_t rmnet_usb_ctrl_read_stats(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -856,6 +1003,7 @@ static ssize_t rmnet_usb_ctrl_read_stats(struct file *file, char __user *ubuf,
"cbits_tolocal: %d\n"
"cbits_tomdm: %d\n"
"mdm_wait_timeout: %u\n"
+ "zlp_cnt: %u\n"
"dev opened: %s\n",
dev, dev->name,
dev->snd_encap_cmd_cnt,
@@ -866,6 +1014,7 @@ static ssize_t rmnet_usb_ctrl_read_stats(struct file *file, char __user *ubuf,
dev->cbits_tolocal,
dev->cbits_tomdm,
dev->mdm_wait_timeout,
+ dev->zlp_cnt,
dev->is_opened ? "OPEN" : "CLOSE");
}
@@ -893,6 +1042,7 @@ static ssize_t rmnet_usb_ctrl_reset_stats(struct file *file, const char __user *
dev->get_encap_resp_cnt = 0;
dev->set_ctrl_line_state_cnt = 0;
dev->tx_ctrl_err_cnt = 0;
+ dev->zlp_cnt = 0;
}
return count;
}
@@ -950,6 +1100,8 @@ int rmnet_usb_ctrl_init(void)
INIT_LIST_HEAD(&dev->rx_list);
init_usb_anchor(&dev->tx_submitted);
+ wake_lock_init(&dev->ctrl_wake, WAKE_LOCK_SUSPEND, dev->name);
+
status = rmnet_usb_ctrl_alloc_rx(dev);
if (status < 0) {
kfree(dev);
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb_ctrl.h
index f6e5876..cc14cff 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.h
+++ b/drivers/net/usb/rmnet_usb_ctrl.h
@@ -18,6 +18,7 @@
#include <linux/cdev.h>
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
+#include <linux/wakelock.h>
#define CTRL_DEV_MAX_LEN 10
@@ -46,6 +47,8 @@ struct rmnet_ctrl_dev {
unsigned is_opened;
+ bool is_connected;
+
/*input control lines (DSR, CTS, CD, RI)*/
unsigned int cbits_tolocal;
@@ -66,6 +69,14 @@ struct rmnet_ctrl_dev {
unsigned int resp_avail_cnt;
unsigned int set_ctrl_line_state_cnt;
unsigned int tx_ctrl_err_cnt;
+ unsigned int zlp_cnt;
+ unsigned int tx_ctrl_in_req_cnt;
+
+ struct wake_lock ctrl_wake;
+
+ /* reset handler */
+ struct notifier_block reset_notifier_block;
+ bool tx_block;
};
extern struct rmnet_ctrl_dev *ctrl_dev[];
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index ae3f934..6333dd0 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -13,12 +13,19 @@
#include <linux/mii.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/usb.h>
#include <linux/usb/usbnet.h>
#include <linux/msm_rmnet.h>
#include "rmnet_usb_ctrl.h"
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
+#endif
+
#define RMNET_DATA_LEN 2000
#define HEADROOM_FOR_QOS 8
@@ -416,6 +423,90 @@ static void rmnet_usb_setup(struct net_device *dev)
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
+static int rmnet_usb_data_status(struct seq_file *s, void *unused)
+{
+ struct usbnet *unet = s->private;
+
+ seq_printf(s, "RMNET_MODE_LLP_IP: %d\n",
+ test_bit(RMNET_MODE_LLP_IP, &unet->data[0]));
+ seq_printf(s, "RMNET_MODE_LLP_ETH: %d\n",
+ test_bit(RMNET_MODE_LLP_ETH, &unet->data[0]));
+ seq_printf(s, "RMNET_MODE_QOS: %d\n",
+ test_bit(RMNET_MODE_QOS, &unet->data[0]));
+ seq_printf(s, "Net MTU: %u\n", unet->net->mtu);
+ seq_printf(s, "rx_urb_size: %u\n", unet->rx_urb_size);
+ seq_printf(s, "rx skb q len: %u\n", unet->rxq.qlen);
+ seq_printf(s, "rx skb done q len: %u\n", unet->done.qlen);
+ seq_printf(s, "rx errors: %lu\n", unet->net->stats.rx_errors);
+ seq_printf(s, "rx over errors: %lu\n",
+ unet->net->stats.rx_over_errors);
+ seq_printf(s, "rx length errors: %lu\n",
+ unet->net->stats.rx_length_errors);
+ seq_printf(s, "rx packets: %lu\n", unet->net->stats.rx_packets);
+ seq_printf(s, "rx bytes: %lu\n", unet->net->stats.rx_bytes);
+ seq_printf(s, "tx skb q len: %u\n", unet->txq.qlen);
+ seq_printf(s, "tx errors: %lu\n", unet->net->stats.tx_errors);
+ seq_printf(s, "tx packets: %lu\n", unet->net->stats.tx_packets);
+ seq_printf(s, "tx bytes: %lu\n", unet->net->stats.tx_bytes);
+ seq_printf(s, "suspend count: %d\n", unet->suspend_count);
+ seq_printf(s, "EVENT_DEV_OPEN: %d\n",
+ test_bit(EVENT_DEV_OPEN, &unet->flags));
+ seq_printf(s, "EVENT_TX_HALT: %d\n",
+ test_bit(EVENT_TX_HALT, &unet->flags));
+ seq_printf(s, "EVENT_RX_HALT: %d\n",
+ test_bit(EVENT_RX_HALT, &unet->flags));
+ seq_printf(s, "EVENT_RX_MEMORY: %d\n",
+ test_bit(EVENT_RX_MEMORY, &unet->flags));
+ seq_printf(s, "EVENT_DEV_ASLEEP: %d\n",
+ test_bit(EVENT_DEV_ASLEEP, &unet->flags));
+
+ return 0;
+}
+
+static int rmnet_usb_data_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rmnet_usb_data_status, inode->i_private);
+}
+
+const struct file_operations rmnet_usb_data_fops = {
+ .open = rmnet_usb_data_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int rmnet_usb_data_debugfs_init(struct usbnet *unet)
+{
+ struct dentry *rmnet_usb_data_dbg_root;
+ struct dentry *rmnet_usb_data_dentry;
+
+ rmnet_usb_data_dbg_root = debugfs_create_dir(unet->net->name, NULL);
+ if (!rmnet_usb_data_dbg_root || IS_ERR(rmnet_usb_data_dbg_root))
+ return -ENODEV;
+
+ rmnet_usb_data_dentry = debugfs_create_file("status",
+ S_IRUGO | S_IWUSR,
+ rmnet_usb_data_dbg_root, unet,
+ &rmnet_usb_data_fops);
+
+ if (!rmnet_usb_data_dentry) {
+ debugfs_remove_recursive(rmnet_usb_data_dbg_root);
+ return -ENODEV;
+ }
+
+ unet->data[2] = (unsigned long)rmnet_usb_data_dbg_root;
+
+ return 0;
+}
+
+static void rmnet_usb_data_debugfs_cleanup(struct usbnet *unet)
+{
+ struct dentry *root = (struct dentry *)unet->data[2];
+
+ debugfs_remove_recursive(root);
+ unet->data[2] = 0;
+}
+
static int rmnet_usb_probe(struct usb_interface *iface,
const struct usb_device_id *prod)
{
@@ -469,6 +560,18 @@ static int rmnet_usb_probe(struct usb_interface *iface,
if (status)
goto out;
+ status = rmnet_usb_data_debugfs_init(unet);
+ if (status)
+ dev_dbg(&udev->dev, "mode debugfs file is not available\n");
+
+#ifdef CONFIG_MDM_HSIC_PM
+ status = register_udev_to_pm_dev(rmnet_pm_dev, udev);
+ if (status) {
+ dev_err(&udev->dev,
+ "%s: fail to register to hsic pm device\n", __func__);
+ goto out;
+ }
+#endif
/* allow modem to wake up suspended system */
device_set_wakeup_enable(&udev->dev, 1);
out:
@@ -490,11 +593,16 @@ static void rmnet_usb_disconnect(struct usb_interface *intf)
return;
}
+ rmnet_usb_data_debugfs_cleanup(unet);
+
dev = (struct rmnet_ctrl_dev *)unet->data[1];
if (!dev) {
dev_err(&udev->dev, "%s:ctrl device not found\n", __func__);
return;
}
+#ifdef CONFIG_MDM_HSIC_PM
+ unregister_udev_from_pm_dev(rmnet_pm_dev, udev);
+#endif
unet->data[0] = 0;
unet->data[1] = 0;
rmnet_usb_ctrl_disconnect(dev);
@@ -560,6 +668,7 @@ static struct usb_driver rmnet_usb = {
.disconnect = rmnet_usb_disconnect,
.suspend = rmnet_usb_suspend,
.resume = rmnet_usb_resume,
+ .reset_resume = rmnet_usb_resume,
.supports_autosuspend = true,
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 8456079..ed9c78b 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -50,6 +50,10 @@
#define DRIVER_VERSION "22-Aug-2005"
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
+#endif
/*-------------------------------------------------------------------------*/
@@ -211,6 +215,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
} else {
usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
buf, maxp, intr_complete, dev, period);
+ dev->interrupt->transfer_flags |= URB_FREE_BUFFER;
dev_dbg(&intf->dev,
"status ep%din, %d bytes period %d\n",
usb_pipeendpoint(pipe), maxp, period);
@@ -241,10 +246,21 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
skb->len + sizeof (struct ethhdr), skb->protocol);
memset (skb->cb, 0, sizeof (struct skb_data));
+
+ if (skb_defer_rx_timestamp(skb))
+ return;
+
status = netif_rx (skb);
if (status != NET_RX_SUCCESS)
netif_dbg(dev, rx_err, dev->net,
"netif_rx status %d\n", status);
+#ifdef CONFIG_MDM_HSIC_PM
+ if (dev->udev->descriptor.idProduct == 0x9048 ||
+ dev->udev->descriptor.idProduct == 0x904C) {
+ pr_debug("rx fast dormancy wakelock\n");
+ fast_dormancy_wakelock(rmnet_pm_dev);
+ }
+#endif
}
EXPORT_SYMBOL_GPL(usbnet_skb_return);
@@ -280,17 +296,32 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
}
EXPORT_SYMBOL_GPL(usbnet_change_mtu);
+/* The caller must hold list->lock */
+static void __usbnet_queue_skb(struct sk_buff_head *list,
+ struct sk_buff *newsk, enum skb_state state)
+{
+ struct skb_data *entry = (struct skb_data *) newsk->cb;
+
+ __skb_queue_tail(list, newsk);
+ entry->state = state;
+}
+
/*-------------------------------------------------------------------------*/
/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
* completion callbacks. 2.5 should have fixed those bugs...
*/
-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+ struct sk_buff_head *list, enum skb_state state)
{
unsigned long flags;
+ enum skb_state old_state;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
spin_lock_irqsave(&list->lock, flags);
+ old_state = entry->state;
+ entry->state = state;
__skb_unlink(skb, list);
spin_unlock(&list->lock);
spin_lock(&dev->done.lock);
@@ -298,6 +329,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea
if (dev->done.qlen == 1)
tasklet_schedule(&dev->bh);
spin_unlock_irqrestore(&dev->done.lock, flags);
+ return old_state;
}
/* some work can't be done in tasklets, so we use keventd
@@ -327,7 +359,8 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
unsigned long lockflags;
size_t size = dev->rx_urb_size;
- if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+ skb = alloc_skb(size + NET_IP_ALIGN, flags);
+ if (!skb) {
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb);
@@ -340,7 +373,6 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = rx_start;
entry->length = 0;
usb_fill_bulk_urb (urb, dev->udev, dev->in,
@@ -373,7 +405,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
break;
case 0:
usb_mark_last_busy(dev->udev);
- __skb_queue_tail (&dev->rxq, skb);
+ __usbnet_queue_skb(&dev->rxq, skb, rx_start);
}
} else {
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -424,16 +456,17 @@ static void rx_complete (struct urb *urb)
struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev;
int urb_status = urb->status;
+ enum skb_state state;
skb_put (skb, urb->actual_length);
- entry->state = rx_done;
+ state = rx_done;
entry->urb = NULL;
switch (urb_status) {
/* success */
case 0:
if (skb->len < dev->net->hard_header_len) {
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
netif_dbg(dev, rx_err, dev->net,
@@ -472,7 +505,7 @@ static void rx_complete (struct urb *urb)
"rx throttle %d\n", urb_status);
}
block:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
entry->urb = urb;
urb = NULL;
break;
@@ -483,18 +516,20 @@ block:
// FALLTHROUGH
default:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
break;
}
- defer_bh(dev, skb, &dev->rxq);
+ state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
if (netif_running (dev->net) &&
- !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ !test_bit(EVENT_RX_HALT, &dev->flags) &&
+ state != unlink_start) {
rx_submit (dev, urb, GFP_ATOMIC);
+ usb_mark_last_busy(dev->udev);
return;
}
usb_free_urb (urb);
@@ -579,16 +614,23 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
unsigned long flags;
- struct sk_buff *skb, *skbnext;
+ struct sk_buff *skb;
int count = 0;
spin_lock_irqsave (&q->lock, flags);
- skb_queue_walk_safe(q, skb, skbnext) {
+ while (!skb_queue_empty(q)) {
struct skb_data *entry;
struct urb *urb;
int retval;
- entry = (struct skb_data *) skb->cb;
+ skb_queue_walk(q, skb) {
+ entry = (struct skb_data *) skb->cb;
+ if (entry->state != unlink_start)
+ goto found;
+ }
+ break;
+found:
+ entry->state = unlink_start;
urb = entry->urb;
/*
@@ -599,16 +641,17 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
* handler(include defer_bh).
*/
usb_get_urb(urb);
- spin_unlock_irqrestore(&q->lock, flags);
- // during some PM-driven resume scenarios,
- // these (async) unlinks complete immediately
+ /* spin_unlock_irqrestore(&q->lock, flags); */
+ /* during some PM-driven resume scenarios,
+ * these (async) unlinks complete immediately
+ */
retval = usb_unlink_urb (urb);
if (retval != -EINPROGRESS && retval != 0)
netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
else
count++;
usb_put_urb(urb);
- spin_lock_irqsave(&q->lock, flags);
+ /* spin_lock_irqsave(&q->lock, flags); */
}
spin_unlock_irqrestore (&q->lock, flags);
return count;
@@ -1004,8 +1047,19 @@ static void tx_complete (struct urb *urb)
if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
dev->net->stats.tx_packets++;
dev->net->stats.tx_bytes += entry->length;
+#ifdef CONFIG_MDM_HSIC_PM
+ if (dev->udev->descriptor.idProduct == 0x9048 ||
+ dev->udev->descriptor.idProduct == 0x904C) {
+ pr_debug("set tx wakelock for fd\n");
+ fast_dormancy_wakelock(rmnet_pm_dev);
+ }
+#endif
} else {
dev->net->stats.tx_errors++;
+ if (dev->udev->descriptor.idProduct == 0x9048 ||
+ dev->udev->descriptor.idProduct == 0x904C)
+ netdev_err(dev->net, "tx err %d, %d\n",
+ urb->status, entry->urb->status);
switch (urb->status) {
case -EPIPE:
@@ -1039,8 +1093,7 @@ static void tx_complete (struct urb *urb)
}
usb_autopm_put_interface_async(dev->intf);
- entry->state = tx_done;
- defer_bh(dev, skb, &dev->txq);
+ (void) defer_bh(dev, skb, &dev->txq, tx_done);
}
/*-------------------------------------------------------------------------*/
@@ -1069,6 +1122,9 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
unsigned long flags;
int retval;
+ if (skb)
+ skb_tx_timestamp(skb);
+
// some devices want funky USB-level framing, for
// win32 driver (usually) and/or hardware quirks
if (info->tx_fixup) {
@@ -1093,7 +1149,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = tx_start;
entry->length = length;
usb_fill_bulk_urb (urb, dev->udev, dev->out,
@@ -1152,7 +1207,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
break;
case 0:
net->trans_start = jiffies;
- __skb_queue_tail (&dev->txq, skb);
+ __usbnet_queue_skb(&dev->txq, skb, tx_start);
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
@@ -1343,10 +1398,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// set up our own records
net = alloc_etherdev(sizeof(*dev));
- if (!net) {
- dbg ("can't kmalloc dev");
+ if (!net)
goto out;
- }
/* netdev_printk() needs this so do it as early as possible */
SET_NETDEV_DEV(net, &udev->dev);
@@ -1443,7 +1496,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
status = register_netdev (net);
if (status)
- goto out3;
+ goto out4;
netif_info(dev, probe, dev->net,
"register '%s' at usb-%s-%s, %s, %pM\n",
udev->dev.driver->name,
@@ -1461,6 +1514,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
return 0;
+out4:
+ usb_free_urb(dev->interrupt);
out3:
if (info->unbind)
info->unbind (dev, udev);
@@ -1544,7 +1599,7 @@ int usbnet_resume (struct usb_interface *intf)
if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
if (!(dev->txq.qlen >= TX_QLEN(dev)))
- netif_start_queue(dev->net);
+ netif_tx_wake_all_queues(dev->net);
tasklet_schedule (&dev->bh);
}
}