aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/diag_bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/misc/diag_bridge.c')
-rw-r--r--drivers/usb/misc/diag_bridge.c154
1 files changed, 140 insertions, 14 deletions
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 9794918..bdd6654 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -23,8 +23,15 @@
#include <linux/debugfs.h>
#include <mach/diag_bridge.h>
+#ifdef CONFIG_MDM_HSIC_PM
+#include <linux/mdm_hsic_pm.h>
+static const char rmnet_pm_dev[] = "mdm_hsic_pm0";
+#endif
+
#define DRIVER_DESC "USB host diag bridge driver"
#define DRIVER_VERSION "1.0"
+/* zero_pky.patch */
+#define IN_BUF_SIZE 16384
struct diag_bridge {
struct usb_device *udev;
@@ -36,6 +43,9 @@ struct diag_bridge {
struct kref kref;
struct diag_bridge_ops *ops;
struct platform_device *pdev;
+ /* zero_pky.patch */
+ unsigned char *buf_in;
+
/* debugging counters */
unsigned long bytes_to_host;
@@ -56,11 +66,72 @@ int diag_bridge_open(struct diag_bridge_ops *ops)
dev->ops = ops;
dev->err = 0;
+ usb_kill_anchored_urbs(&dev->submitted);
return 0;
}
EXPORT_SYMBOL(diag_bridge_open);
+/* zero_pky.patch */
+/* Even when no driver is using the diag bridge
+ we are setting default read on this endpoint.
+ This will consume any packet sent by CP
+ and its dropped */
+static void read_hsic_cb(struct urb* urb);
+static void read_hsic(void)
+{
+ struct diag_bridge *dev = __dev;
+ struct urb *urb = NULL;
+ unsigned int pipe;
+ int ret;
+
+ dev_info(&dev->udev->dev, "%s:\n", __func__);
+ if (!dev->ifc) {
+ dev_err(&dev->udev->dev, "device is disconnected\n");
+ return;
+ }
+
+ /* if there was a previous unrecoverable error, just quit */
+ if (dev->err)
+ return;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&dev->udev->dev, "unable to allocate urb\n");
+ return;
+ }
+
+ pipe = usb_rcvbulkpipe(dev->udev, dev->in_epAddr);
+ usb_fill_bulk_urb(urb, dev->udev, pipe, dev->buf_in,IN_BUF_SIZE,
+ read_hsic_cb, dev);
+ usb_anchor_urb(urb, &dev->submitted);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ dev_err(&dev->udev->dev, "submitting urb failed err:%d\n", ret);
+ dev->pending_reads--;
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ return;
+ }
+
+ usb_free_urb(urb);
+}
+
+static void read_hsic_cb(struct urb *urb)
+{
+ struct diag_bridge *dev = urb->context;
+ struct diag_bridge_ops *cbs = dev->ops;
+
+ pr_info("%s: status:%d actual:%d\n", __func__,
+ urb->status, urb->actual_length);
+
+ /* Drop the packet */
+ if (urb->status == -EPROTO) {
+ pr_err("%s: drop packet from protocol error\n", __func__);
+ return;
+ }
+}
+
void diag_bridge_close(void)
{
struct diag_bridge *dev = __dev;
@@ -78,9 +149,6 @@ static void diag_bridge_read_cb(struct urb *urb)
struct diag_bridge *dev = urb->context;
struct diag_bridge_ops *cbs = dev->ops;
- dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
- urb->status, urb->actual_length);
-
if (urb->status == -EPROTO) {
dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
/* save error so that subsequent read/write returns ESHUTDOWN */
@@ -88,7 +156,8 @@ static void diag_bridge_read_cb(struct urb *urb)
return;
}
- cbs->read_complete_cb(cbs->ctxt,
+ if (cbs && cbs->read_complete_cb)
+ cbs->read_complete_cb(cbs->ctxt,
urb->transfer_buffer,
urb->transfer_buffer_length,
urb->status < 0 ? urb->status : urb->actual_length);
@@ -103,8 +172,10 @@ int diag_bridge_read(char *data, int size)
unsigned int pipe;
struct diag_bridge *dev = __dev;
int ret;
+ int spin = 50;
- dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+ if (!dev || !dev->udev)
+ return -ENODEV;
if (!size) {
dev_err(&dev->udev->dev, "invalid size:%d\n", size);
@@ -120,6 +191,16 @@ int diag_bridge_read(char *data, int size)
if (dev->err)
return -ESHUTDOWN;
+ while (check_request_blocked(rmnet_pm_dev) && spin--) {
+ pr_debug("%s: wake up wait loop\n", __func__);
+ msleep(20);
+ }
+
+ if (check_request_blocked(rmnet_pm_dev)) {
+ pr_err("%s: in lpa wakeup, return EAGAIN\n", __func__);
+ return -EAGAIN;
+ }
+
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
dev_err(&dev->udev->dev, "unable to allocate urb\n");
@@ -161,24 +242,25 @@ static void diag_bridge_write_cb(struct urb *urb)
struct diag_bridge *dev = urb->context;
struct diag_bridge_ops *cbs = dev->ops;
- dev_dbg(&dev->udev->dev, "%s:\n", __func__);
-
usb_autopm_put_interface_async(dev->ifc);
if (urb->status == -EPROTO) {
dev_err(&dev->udev->dev, "%s: proto error\n", __func__);
/* save error so that subsequent read/write returns ESHUTDOWN */
dev->err = urb->status;
+ usb_free_urb(urb);
return;
}
- cbs->write_complete_cb(cbs->ctxt,
+ if (cbs && cbs->write_complete_cb)
+ cbs->write_complete_cb(cbs->ctxt,
urb->transfer_buffer,
urb->transfer_buffer_length,
urb->status < 0 ? urb->status : urb->actual_length);
dev->bytes_to_mdm += urb->actual_length;
dev->pending_writes--;
+ usb_free_urb(urb);
}
int diag_bridge_write(char *data, int size)
@@ -187,8 +269,10 @@ int diag_bridge_write(char *data, int size)
unsigned int pipe;
struct diag_bridge *dev = __dev;
int ret;
+ int spin;
- dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+ if (!dev || !dev->udev)
+ return -ENODEV;
if (!size) {
dev_err(&dev->udev->dev, "invalid size:%d\n", size);
@@ -204,19 +288,48 @@ int diag_bridge_write(char *data, int size)
if (dev->err)
return -ESHUTDOWN;
+ spin = 50;
+ while (check_request_blocked(rmnet_pm_dev) && spin--) {
+ pr_info("%s: wake up wait loop\n", __func__);
+ msleep(20);
+ }
+
+ if (check_request_blocked(rmnet_pm_dev)) {
+ pr_err("%s: in lpa wakeup, return EAGAIN\n", __func__);
+ return -EAGAIN;
+ }
+
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
err("unable to allocate urb");
return -ENOMEM;
}
- ret = usb_autopm_get_interface(dev->ifc);
+ ret = usb_autopm_get_interface_async(dev->ifc);
if (ret < 0) {
dev_err(&dev->udev->dev, "autopm_get failed:%d\n", ret);
usb_free_urb(urb);
return ret;
}
+ for (spin = 0; spin < 10; spin++) {
+ /* check rpm active */
+ if (dev->udev->dev.power.runtime_status == RPM_ACTIVE) {
+ ret = 0;
+ break;
+ } else {
+ dev_err(&dev->udev->dev, "waiting rpm active\n");
+ ret = -EAGAIN;
+ }
+ msleep(20);
+ }
+ if (ret < 0) {
+ dev_err(&dev->udev->dev, "rpm active failed:%d\n", ret);
+ usb_free_urb(urb);
+ usb_autopm_put_interface(dev->ifc);
+ return ret;
+ }
+
pipe = usb_sndbulkpipe(dev->udev, dev->out_epAddr);
usb_fill_bulk_urb(urb, dev->udev, pipe, data, size,
diag_bridge_write_cb, dev);
@@ -232,9 +345,9 @@ int diag_bridge_write(char *data, int size)
usb_autopm_put_interface(dev->ifc);
return ret;
}
-
+#if 0
usb_free_urb(urb);
-
+#endif
return 0;
}
EXPORT_SYMBOL(diag_bridge_write);
@@ -351,6 +464,12 @@ diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
kfree(dev);
return -ENOMEM;
}
+ /* zero_pky.patch */
+ dev->buf_in = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+ if (!dev->buf_in) {
+ pr_err("%s: unable to allocate dev->buf_in\n", __func__);
+ return -ENOMEM;
+ }
__dev = dev;
dev->udev = usb_get_dev(interface_to_usbdev(ifc));
@@ -397,6 +516,8 @@ static void diag_bridge_disconnect(struct usb_interface *ifc)
dev_dbg(&dev->udev->dev, "%s:\n", __func__);
platform_device_del(dev->pdev);
+ /* zero_pky.patch */
+ kfree(dev->buf_in);
diag_bridge_debugfs_cleanup();
kref_put(&dev->kref, diag_bridge_delete);
usb_set_intfdata(ifc, NULL);
@@ -415,10 +536,11 @@ static int diag_bridge_suspend(struct usb_interface *ifc, pm_message_t message)
"%s: diag veto'd suspend\n", __func__);
return ret;
}
-
- usb_kill_anchored_urbs(&dev->submitted);
}
+ /* zero_pky.patch */
+ usb_kill_anchored_urbs(&dev->submitted);
+
return ret;
}
@@ -430,6 +552,9 @@ static int diag_bridge_resume(struct usb_interface *ifc)
if (cbs && cbs->resume)
cbs->resume(cbs->ctxt);
+ /* set the default read */ /* zero_pky.patch */
+ else
+ read_hsic();
return 0;
}
@@ -455,6 +580,7 @@ static struct usb_driver diag_bridge_driver = {
.disconnect = diag_bridge_disconnect,
.suspend = diag_bridge_suspend,
.resume = diag_bridge_resume,
+ .reset_resume = diag_bridge_resume,
.id_table = diag_bridge_ids,
.supports_autosuspend = 1,
};