diff options
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/rmnet_usb_ctrl.c | 198 | ||||
-rw-r--r-- | drivers/net/usb/rmnet_usb_ctrl.h | 11 | ||||
-rw-r--r-- | drivers/net/usb/rmnet_usb_data.c | 109 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 107 |
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); } } |