Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
new file mode 100644
index 0000000..23ba900
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -0,0 +1,1060 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#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/ratelimit.h>
+#include <linux/debugfs.h>
+#include "rmnet_usb_ctrl.h"
+
+#define DEVICE_NAME			"hsicctl"
+#define NUM_CTRL_CHANNELS		4
+#define DEFAULT_READ_URB_LENGTH		0x1000
+
+/*Output control lines.*/
+#define ACM_CTRL_DTR		BIT(0)
+#define ACM_CTRL_RTS		BIT(1)
+
+
+/*Input control lines.*/
+#define ACM_CTRL_DSR		BIT(0)
+#define ACM_CTRL_CTS		BIT(1)
+#define ACM_CTRL_RI		BIT(2)
+#define ACM_CTRL_CD		BIT(3)
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL		7
+#define FS_LS_INTERVAL		3
+
+/*echo modem_wait > /sys/class/hsicctl/hsicctlx/modem_wait*/
+static ssize_t modem_wait_store(struct device *d, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	unsigned int		mdm_wait;
+	struct rmnet_ctrl_dev	*dev = dev_get_drvdata(d);
+
+	if (!dev)
+		return -ENODEV;
+
+	sscanf(buf, "%u", &mdm_wait);
+
+	dev->mdm_wait_timeout = mdm_wait;
+
+	return n;
+}
+
+static ssize_t modem_wait_show(struct device *d, struct device_attribute *attr,
+		char *buf)
+{
+	struct rmnet_ctrl_dev	*dev = dev_get_drvdata(d);
+
+	if (!dev)
+		return -ENODEV;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", dev->mdm_wait_timeout);
+}
+
+static DEVICE_ATTR(modem_wait, 0664, modem_wait_show, modem_wait_store);
+
+static int	ctl_msg_dbg_mask;
+module_param_named(dump_ctrl_msg, ctl_msg_dbg_mask, int,
+		S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+	MSM_USB_CTL_DEBUG = 1U << 0,
+	MSM_USB_CTL_DUMP_BUFFER = 1U << 1,
+};
+
+#define DUMP_BUFFER(prestr, cnt, buf) \
+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); \
+} while (0)
+
+#define DBG(x...) \
+		do { \
+			if (ctl_msg_dbg_mask & MSM_USB_CTL_DEBUG) \
+				pr_info(x); \
+		} while (0)
+
+struct rmnet_ctrl_dev		*ctrl_dev[NUM_CTRL_CHANNELS];
+struct class			*ctrldev_classp;
+static dev_t			ctrldev_num;
+
+struct ctrl_pkt {
+	size_t	data_size;
+	void	*data;
+};
+
+struct ctrl_pkt_list_elem {
+	struct list_head	list;
+	struct ctrl_pkt		cpkt;
+};
+
+static void resp_avail_cb(struct urb *);
+
+static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+{
+	if (dev) {
+		mutex_lock(&dev->dev_lock);
+		if (!dev->intf) {
+			mutex_unlock(&dev->dev_lock);
+			return 0;
+		}
+		mutex_unlock(&dev->dev_lock);
+		return 1;
+	}
+	return 0;
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+	int				status;
+	struct usb_cdc_notification	*ctrl;
+	struct usb_device		*udev;
+	struct rmnet_ctrl_dev		*dev = urb->context;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		return;
+	case -EPIPE:
+		pr_err_ratelimited("%s: Stall on int endpoint\n", __func__);
+		/* TBD : halt to be cleared in work */
+		return;
+
+	/*resubmit*/
+	case -EOVERFLOW:
+		pr_err_ratelimited("%s: Babble error happened\n", __func__);
+	default:
+		 pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+			__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	ctrl = urb->transfer_buffer;
+
+	switch (ctrl->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		dev->resp_avail_cnt++;
+		usb_fill_control_urb(dev->rcvurb, udev,
+					usb_rcvctrlpipe(udev, 0),
+					(unsigned char *)dev->in_ctlreq,
+					dev->rcvbuf,
+					DEFAULT_READ_URB_LENGTH,
+					resp_avail_cb, dev);
+
+		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;
+		}
+
+		if (!dev->resp_available) {
+			dev->resp_available = true;
+			wake_up(&dev->open_wait_queue);
+		}
+
+		return;
+	default:
+		 dev_err(dev->devicep,
+			"%s:Command not implemented\n", __func__);
+	}
+
+resubmit_int_urb:
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status)
+		dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+		__func__, status);
+
+	return;
+}
+
+static void resp_avail_cb(struct urb *urb)
+{
+	struct usb_device		*udev;
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	struct rmnet_ctrl_dev		*dev = urb->context;
+	void				*cpkt;
+	int				status = 0;
+	size_t				cpkt_size = 0;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	switch (urb->status) {
+	case 0:
+		/*success*/
+		dev->get_encap_resp_cnt++;
+		break;
+
+	/*do not resubmit*/
+	case -ESHUTDOWN:
+	case -ENOENT:
+	case -ECONNRESET:
+	case -EPROTO:
+		return;
+
+	/*resubmit*/
+	case -EOVERFLOW:
+		pr_err_ratelimited("%s: Babble error happened\n", __func__);
+	default:
+		pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+				__func__, urb->status);
+		goto resubmit_int_urb;
+	}
+
+	dev_dbg(dev->devicep, "Read %d bytes for %s\n",
+		urb->actual_length, dev->name);
+
+	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) {
+		dev_err(dev->devicep, "%s: list_elem alloc failed\n", __func__);
+		return;
+	}
+	list_elem->cpkt.data = kmalloc(cpkt_size, GFP_ATOMIC);
+	if (!list_elem->cpkt.data) {
+		dev_err(dev->devicep, "%s: list_elem->data alloc failed\n",
+			__func__);
+		kfree(list_elem);
+		return;
+	}
+	memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
+	list_elem->cpkt.data_size = cpkt_size;
+	spin_lock(&dev->rx_lock);
+	list_add_tail(&list_elem->list, &dev->rx_list);
+	spin_unlock(&dev->rx_lock);
+
+	wake_up(&dev->read_wait_queue);
+
+resubmit_int_urb:
+	/*re-submit int urb to check response available*/
+	status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+	if (status)
+		dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+			__func__, status);
+}
+
+static int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *dev)
+{
+	int	retval = 0;
+
+	retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+	if (retval < 0)
+		dev_err(dev->devicep, "%s Intr submit %d\n", __func__, retval);
+
+	return retval;
+}
+
+int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *dev)
+{
+	if (!is_dev_connected(dev)) {
+		dev_dbg(dev->devicep, "%s: Ctrl device disconnected\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(dev->devicep, "%s\n", __func__);
+
+	usb_kill_urb(dev->rcvurb);
+	usb_kill_urb(dev->inturb);
+
+	return 0;
+}
+
+int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *dev)
+{
+	int	status = 0;
+
+	mutex_lock(&dev->dev_lock);
+	if (dev->is_opened)
+		status = rmnet_usb_ctrl_start_rx(dev);
+	mutex_unlock(&dev->dev_lock);
+
+	return status;
+}
+
+static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
+{
+	int	retval = -ENOMEM;
+
+	dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->rcvurb) {
+		pr_err("%s: Error allocating read urb\n", __func__);
+		goto nomem;
+	}
+
+	dev->rcvbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+	if (!dev->rcvbuf) {
+		pr_err("%s: Error allocating read buffer\n", __func__);
+		goto nomem;
+	}
+
+	dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+	if (!dev->in_ctlreq) {
+		pr_err("%s: Error allocating setup packet buffer\n", __func__);
+		goto nomem;
+	}
+
+	return 0;
+
+nomem:
+	usb_free_urb(dev->rcvurb);
+	kfree(dev->rcvbuf);
+	kfree(dev->in_ctlreq);
+
+	return retval;
+
+}
+static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
+{
+	struct usb_device	*udev;
+
+	if (!is_dev_connected(dev))
+		return -ENODEV;
+
+	udev = interface_to_usbdev(dev->intf);
+	dev->set_ctrl_line_state_cnt++;
+	return 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);
+}
+
+static void ctrl_write_callback(struct urb *urb)
+{
+	struct rmnet_ctrl_dev	*dev = urb->context;
+
+	if (urb->status) {
+		dev->tx_ctrl_err_cnt++;
+		pr_debug_ratelimited("Write status/size %d/%d\n",
+				urb->status, urb->actual_length);
+	}
+
+	kfree(urb->setup_packet);
+	kfree(urb->transfer_buffer);
+	usb_free_urb(urb);
+	usb_autopm_put_interface_async(dev->intf);
+}
+
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
+		size_t size)
+{
+	int			result;
+	struct urb		*sndurb;
+	struct usb_ctrlrequest	*out_ctlreq;
+	struct usb_device	*udev;
+
+	if (!is_dev_connected(dev))
+		return -ENETRESET;
+
+	udev = interface_to_usbdev(dev->intf);
+
+	sndurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!sndurb) {
+		dev_err(dev->devicep, "Error allocating read urb\n");
+		return -ENOMEM;
+	}
+
+	out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_KERNEL);
+	if (!out_ctlreq) {
+		usb_free_urb(sndurb);
+		dev_err(dev->devicep, "Error allocating setup packet buffer\n");
+		return -ENOMEM;
+	}
+
+	/* CDC Send Encapsulated Request packet */
+	out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+			     USB_RECIP_INTERFACE);
+	out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+	out_ctlreq->wValue = 0;
+	out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	out_ctlreq->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(sndurb, udev,
+			     usb_sndctrlpipe(udev, 0),
+			     (unsigned char *)out_ctlreq, (void *)buf, size,
+			     ctrl_write_callback, dev);
+
+	result = usb_autopm_get_interface(dev->intf);
+	if (result < 0) {
+		dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+			__func__, result);
+
+		/*
+		* Revisit:  if (result == -EPERM)
+		*		rmnet_usb_suspend(dev->intf, PMSG_SUSPEND);
+		*/
+
+		usb_free_urb(sndurb);
+		kfree(out_ctlreq);
+		return result;
+	}
+
+	usb_anchor_urb(sndurb, &dev->tx_submitted);
+	dev->snd_encap_cmd_cnt++;
+	result = usb_submit_urb(sndurb, GFP_KERNEL);
+	if (result < 0) {
+		dev_err(dev->devicep, "%s: Submit URB error %d\n",
+			__func__, result);
+		dev->snd_encap_cmd_cnt--;
+		usb_autopm_put_interface(dev->intf);
+		usb_unanchor_urb(sndurb);
+		usb_free_urb(sndurb);
+		kfree(out_ctlreq);
+		return result;
+	}
+
+	return size;
+}
+
+static int rmnet_ctl_open(struct inode *inode, struct file *file)
+{
+	int			retval = 0;
+	struct rmnet_ctrl_dev	*dev =
+		container_of(inode->i_cdev, struct rmnet_ctrl_dev, cdev);
+
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->is_opened)
+		goto already_opened;
+
+	/*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,
+					msecs_to_jiffies(dev->mdm_wait_timeout *
+									1000));
+		if (retval == 0) {
+			dev_err(dev->devicep, "%s: Timeout opening %s\n",
+						__func__, dev->name);
+			return -ETIMEDOUT;
+		} else if (retval < 0) {
+			dev_err(dev->devicep, "%s: Error waiting for %s\n",
+						__func__, dev->name);
+			return retval;
+		}
+	}
+
+	if (!dev->resp_available) {
+		dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
+					__func__, dev->name);
+		return -ETIMEDOUT;
+	}
+
+	mutex_lock(&dev->dev_lock);
+	dev->is_opened = 1;
+	mutex_unlock(&dev->dev_lock);
+
+	file->private_data = dev;
+
+already_opened:
+	DBG("%s: Open called for %s\n", __func__, dev->name);
+
+	return 0;
+}
+
+static int rmnet_ctl_release(struct inode *inode, struct file *file)
+{
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	struct rmnet_ctrl_dev		*dev;
+	unsigned long			flag;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	DBG("%s Called on %s device\n", __func__, dev->name);
+
+	spin_lock_irqsave(&dev->rx_lock, flag);
+	while (!list_empty(&dev->rx_list)) {
+		list_elem = list_first_entry(
+				&dev->rx_list,
+				struct ctrl_pkt_list_elem,
+				list);
+		list_del(&list_elem->list);
+		kfree(list_elem->cpkt.data);
+		kfree(list_elem);
+	}
+	spin_unlock_irqrestore(&dev->rx_lock, flag);
+
+	mutex_lock(&dev->dev_lock);
+	dev->is_opened = 0;
+	mutex_unlock(&dev->dev_lock);
+
+	rmnet_usb_ctrl_stop_rx(dev);
+
+	if (is_dev_connected(dev))
+		usb_kill_anchored_urbs(&dev->tx_submitted);
+
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static ssize_t rmnet_ctl_read(struct file *file, char __user *buf, size_t count,
+		loff_t *ppos)
+{
+	int				retval = 0;
+	int				bytes_to_read;
+	struct rmnet_ctrl_dev		*dev;
+	struct ctrl_pkt_list_elem	*list_elem = NULL;
+	unsigned long			flags;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	DBG("%s: Read from %s\n", __func__, dev->name);
+
+ctrl_read:
+	if (!is_dev_connected(dev)) {
+		dev_dbg(dev->devicep, "%s: Device not connected\n",
+			__func__);
+		return -ENETRESET;
+	}
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	if (list_empty(&dev->rx_list)) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+		retval = wait_event_interruptible(dev->read_wait_queue,
+					!list_empty(&dev->rx_list) ||
+					!is_dev_connected(dev));
+		if (retval < 0)
+			return retval;
+
+		goto ctrl_read;
+	}
+
+	list_elem = list_first_entry(&dev->rx_list,
+				     struct ctrl_pkt_list_elem, list);
+	bytes_to_read = (uint32_t)(list_elem->cpkt.data_size);
+	if (bytes_to_read > count) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+		dev_err(dev->devicep, "%s: Packet size %d > buf size %d\n",
+			__func__, bytes_to_read, count);
+		return -ENOMEM;
+	}
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+	if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+			dev_err(dev->devicep,
+				"%s: copy_to_user failed for %s\n",
+				__func__, dev->name);
+		return -EFAULT;
+	}
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	list_del(&list_elem->list);
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+	kfree(list_elem->cpkt.data);
+	kfree(list_elem);
+	DBG("%s: Returning %d bytes to %s\n", __func__, bytes_to_read,
+			dev->name);
+	DUMP_BUFFER("Read: ", bytes_to_read, buf);
+
+	return bytes_to_read;
+}
+
+static ssize_t rmnet_ctl_write(struct file *file, const char __user * buf,
+		size_t size, loff_t *pos)
+{
+	int			status;
+	void			*wbuf;
+	struct rmnet_ctrl_dev	*dev = file->private_data;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (size <= 0)
+		return -EINVAL;
+
+	if (!is_dev_connected(dev))
+		return -ENETRESET;
+
+	DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
+
+	wbuf = kmalloc(size , GFP_KERNEL);
+	if (!wbuf)
+		return -ENOMEM;
+
+	status = copy_from_user(wbuf , buf, size);
+	if (status) {
+		dev_err(dev->devicep,
+		"%s: Unable to copy data from userspace %d\n",
+		__func__, status);
+		kfree(wbuf);
+		return status;
+	}
+	DUMP_BUFFER("Write: ", size, buf);
+
+	status = rmnet_usb_ctrl_write(dev, wbuf, size);
+	if (status == size)
+		return size;
+
+	return status;
+}
+
+static int rmnet_ctrl_tiocmset(struct rmnet_ctrl_dev *dev, unsigned int set,
+		unsigned int clear)
+{
+	int retval;
+
+	mutex_lock(&dev->dev_lock);
+	if (set & TIOCM_DTR)
+		dev->cbits_tomdm |= ACM_CTRL_DTR;
+
+	/*
+	 * TBD if (set & TIOCM_RTS)
+	 *	dev->cbits_tomdm |= ACM_CTRL_RTS;
+	 */
+
+	if (clear & TIOCM_DTR)
+		dev->cbits_tomdm &= ~ACM_CTRL_DTR;
+
+	/*
+	 * (clear & TIOCM_RTS)
+	 *	dev->cbits_tomdm &= ~ACM_CTRL_RTS;
+	 */
+
+	mutex_unlock(&dev->dev_lock);
+
+	retval = usb_autopm_get_interface(dev->intf);
+	if (retval < 0) {
+		dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	retval = rmnet_usb_ctrl_write_cmd(dev);
+
+	usb_autopm_put_interface(dev->intf);
+	return retval;
+}
+
+static int rmnet_ctrl_tiocmget(struct rmnet_ctrl_dev *dev)
+{
+	int	ret;
+
+	mutex_lock(&dev->dev_lock);
+	ret =
+	/*
+	 * TBD(dev->cbits_tolocal & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
+	 * (dev->cbits_tolocal & ACM_CTRL_CTS ? TIOCM_CTS : 0) |
+	 */
+	(dev->cbits_tolocal & ACM_CTRL_CD ? TIOCM_CD : 0) |
+	/*
+	 * TBD (dev->cbits_tolocal & ACM_CTRL_RI ? TIOCM_RI : 0) |
+	 *(dev->cbits_tomdm & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
+	*/
+	(dev->cbits_tomdm & ACM_CTRL_DTR ? TIOCM_DTR : 0);
+	mutex_unlock(&dev->dev_lock);
+
+	return ret;
+}
+
+static long rmnet_ctrl_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	int			ret;
+	struct rmnet_ctrl_dev	*dev;
+
+	dev = file->private_data;
+	if (!dev)
+		return -ENODEV;
+
+	switch (cmd) {
+	case TIOCMGET:
+
+		ret = rmnet_ctrl_tiocmget(dev);
+		break;
+	case TIOCMSET:
+		ret = rmnet_ctrl_tiocmset(dev, arg, ~arg);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct file_operations ctrldev_fops = {
+	.owner = THIS_MODULE,
+	.read  = rmnet_ctl_read,
+	.write = rmnet_ctl_write,
+	.unlocked_ioctl = rmnet_ctrl_ioctl,
+	.open  = rmnet_ctl_open,
+	.release = rmnet_ctl_release,
+};
+
+int rmnet_usb_ctrl_probe(struct usb_interface *intf,
+		struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+{
+	u16				wMaxPacketSize;
+	struct usb_endpoint_descriptor	*ep;
+	struct usb_device		*udev;
+	int				interval;
+	int				ret = 0;
+
+	udev = interface_to_usbdev(intf);
+
+	if (!dev) {
+		pr_err("%s: Ctrl device not found\n", __func__);
+		return -ENODEV;
+	}
+	dev->int_pipe = usb_rcvintpipe(udev,
+		int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	mutex_lock(&dev->dev_lock);
+	dev->intf = intf;
+
+	/*TBD: for now just update CD status*/
+	dev->cbits_tolocal = ACM_CTRL_CD;
+
+	/*send DTR high to modem*/
+	dev->cbits_tomdm = ACM_CTRL_DTR;
+	mutex_unlock(&dev->dev_lock);
+
+	dev->resp_available = false;
+	dev->snd_encap_cmd_cnt = 0;
+	dev->get_encap_resp_cnt = 0;
+	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);
+	if (ret < 0)
+		return ret;
+
+	dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->inturb) {
+		dev_err(dev->devicep, "Error allocating int urb\n");
+		return -ENOMEM;
+	}
+
+	/*use max pkt size from ep desc*/
+	ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+	wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+	dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+	if (!dev->intbuf) {
+		usb_free_urb(dev->inturb);
+		dev_err(dev->devicep, "Error allocating int buffer\n");
+		return -ENOMEM;
+	}
+
+	dev->in_ctlreq->bRequestType =
+		(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+	dev->in_ctlreq->bRequest  = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+	dev->in_ctlreq->wValue = 0;
+	dev->in_ctlreq->wIndex =
+		dev->intf->cur_altsetting->desc.bInterfaceNumber;
+	dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
+
+	interval = max((int)int_in->desc.bInterval,
+			(udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL
+							: FS_LS_INTERVAL);
+
+	usb_fill_int_urb(dev->inturb, udev,
+			 dev->int_pipe,
+			 dev->intbuf, wMaxPacketSize,
+			 notification_available_cb, dev, interval);
+
+	return rmnet_usb_ctrl_start_rx(dev);
+}
+
+void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
+{
+	rmnet_usb_ctrl_stop_rx(dev);
+
+	mutex_lock(&dev->dev_lock);
+
+	/*TBD: for now just update CD status*/
+	dev->cbits_tolocal = ~ACM_CTRL_CD;
+
+	dev->cbits_tomdm = ~ACM_CTRL_DTR;
+	dev->intf = NULL;
+	mutex_unlock(&dev->dev_lock);
+
+	wake_up(&dev->read_wait_queue);
+
+	usb_free_urb(dev->inturb);
+	dev->inturb = NULL;
+
+	kfree(dev->intbuf);
+	dev->intbuf = NULL;
+
+	usb_kill_anchored_urbs(&dev->tx_submitted);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#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)
+{
+	struct rmnet_ctrl_dev	*dev;
+	char			*buf;
+	int			ret;
+	int			i;
+	int			temp = 0;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+		dev = ctrl_dev[i];
+		if (!dev)
+			continue;
+
+		temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+				"\n#ctrl_dev: %p     Name: %s#\n"
+				"snd encap cmd cnt         %u\n"
+				"resp avail cnt:           %u\n"
+				"get encap resp cnt:       %u\n"
+				"set ctrl line state cnt:  %u\n"
+				"tx_err_cnt:               %u\n"
+				"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,
+				dev->resp_avail_cnt,
+				dev->get_encap_resp_cnt,
+				dev->set_ctrl_line_state_cnt,
+				dev->tx_ctrl_err_cnt,
+				dev->cbits_tolocal,
+				dev->cbits_tomdm,
+				dev->mdm_wait_timeout,
+				dev->zlp_cnt,
+				dev->is_opened ? "OPEN" : "CLOSE");
+
+	}
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t rmnet_usb_ctrl_reset_stats(struct file *file, const char __user *
+		buf, size_t count, loff_t *ppos)
+{
+	struct rmnet_ctrl_dev	*dev;
+	int			i;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+		dev = ctrl_dev[i];
+		if (!dev)
+			continue;
+
+		dev->snd_encap_cmd_cnt = 0;
+		dev->resp_avail_cnt = 0;
+		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;
+}
+
+const struct file_operations rmnet_usb_ctrl_stats_ops = {
+	.read = rmnet_usb_ctrl_read_stats,
+	.write = rmnet_usb_ctrl_reset_stats,
+};
+
+struct dentry	*usb_ctrl_dent;
+struct dentry	*usb_ctrl_dfile;
+static void rmnet_usb_ctrl_debugfs_init(void)
+{
+	usb_ctrl_dent = debugfs_create_dir("rmnet_usb_ctrl", 0);
+	if (IS_ERR(usb_ctrl_dent))
+		return;
+
+	usb_ctrl_dfile = debugfs_create_file("status", 0644, usb_ctrl_dent, 0,
+			&rmnet_usb_ctrl_stats_ops);
+	if (!usb_ctrl_dfile || IS_ERR(usb_ctrl_dfile))
+		debugfs_remove(usb_ctrl_dent);
+}
+
+static void rmnet_usb_ctrl_debugfs_exit(void)
+{
+	debugfs_remove(usb_ctrl_dfile);
+	debugfs_remove(usb_ctrl_dent);
+}
+
+#else
+static void rmnet_usb_ctrl_debugfs_init(void) { }
+static void rmnet_usb_ctrl_debugfs_exit(void) { }
+#endif
+
+int rmnet_usb_ctrl_init(void)
+{
+	struct rmnet_ctrl_dev	*dev;
+	int			n;
+	int			status;
+
+	for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+
+		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+		if (!dev) {
+			status = -ENOMEM;
+			goto error0;
+		}
+		/*for debug purpose*/
+		snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+
+		mutex_init(&dev->dev_lock);
+		spin_lock_init(&dev->rx_lock);
+		init_waitqueue_head(&dev->read_wait_queue);
+		init_waitqueue_head(&dev->open_wait_queue);
+		INIT_LIST_HEAD(&dev->rx_list);
+		init_usb_anchor(&dev->tx_submitted);
+
+		status = rmnet_usb_ctrl_alloc_rx(dev);
+		if (status < 0) {
+			kfree(dev);
+			goto error0;
+		}
+
+		ctrl_dev[n] = dev;
+	}
+
+	status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
+			DEVICE_NAME);
+	if (IS_ERR_VALUE(status)) {
+		pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+		       __func__, status);
+		goto error0;
+	}
+
+	ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(ctrldev_classp)) {
+		pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+		status = -ENOMEM;
+		goto error1;
+	}
+	for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+		cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
+		ctrl_dev[n]->cdev.owner = THIS_MODULE;
+
+		status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
+
+		if (IS_ERR_VALUE(status)) {
+			pr_err("%s: cdev_add() ret %i\n", __func__, status);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+
+		ctrl_dev[n]->devicep =
+				device_create(ctrldev_classp, NULL,
+				(ctrldev_num + n), NULL,
+				DEVICE_NAME "%d", n);
+
+		if (IS_ERR(ctrl_dev[n]->devicep)) {
+			pr_err("%s: device_create() ENOMEM\n", __func__);
+			status = -ENOMEM;
+			cdev_del(&ctrl_dev[n]->cdev);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+		/*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+		status = device_create_file(ctrl_dev[n]->devicep,
+					&dev_attr_modem_wait);
+		if (status) {
+			device_destroy(ctrldev_classp,
+				MKDEV(MAJOR(ctrldev_num), n));
+			cdev_del(&ctrl_dev[n]->cdev);
+			kfree(ctrl_dev[n]);
+			goto error2;
+		}
+		dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
+	}
+
+	rmnet_usb_ctrl_debugfs_init();
+	pr_info("rmnet usb ctrl Initialized.\n");
+	return 0;
+
+error2:
+		while (--n >= 0) {
+			cdev_del(&ctrl_dev[n]->cdev);
+			device_destroy(ctrldev_classp,
+				MKDEV(MAJOR(ctrldev_num), n));
+		}
+
+		class_destroy(ctrldev_classp);
+		n = NUM_CTRL_CHANNELS;
+error1:
+	unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+error0:
+	while (--n >= 0)
+		kfree(ctrl_dev[n]);
+
+	return status;
+}
+
+void rmnet_usb_ctrl_exit(void)
+{
+	int	i;
+
+	for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
+		if (!ctrl_dev[i])
+			return;
+
+		kfree(ctrl_dev[i]->in_ctlreq);
+		kfree(ctrl_dev[i]->rcvbuf);
+		kfree(ctrl_dev[i]->intbuf);
+		usb_free_urb(ctrl_dev[i]->rcvurb);
+		usb_free_urb(ctrl_dev[i]->inturb);
+#if defined(DEBUG)
+		device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
+#endif
+		cdev_del(&ctrl_dev[i]->cdev);
+		kfree(ctrl_dev[i]);
+		ctrl_dev[i] = NULL;
+		device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+	}
+
+	class_destroy(ctrldev_classp);
+	unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+	rmnet_usb_ctrl_debugfs_exit();
+}