aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/rmnet_usb_ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb/rmnet_usb_ctrl.c')
-rw-r--r--drivers/net/usb/rmnet_usb_ctrl.c198
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);