diff options
Diffstat (limited to 'drivers/net/usb/rmnet_usb_ctrl.c')
-rw-r--r-- | drivers/net/usb/rmnet_usb_ctrl.c | 198 |
1 files changed, 175 insertions, 23 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); |