Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 7d10ae3..b55746c 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -64,6 +64,8 @@
 
 source "drivers/tty/serial/Kconfig"
 
+source "drivers/char/diag/Kconfig"
+
 config TTY_PRINTK
 	bool "TTY driver to output user messages via printk"
 	depends on EXPERT
@@ -633,5 +635,46 @@
 	  Enables userspace clients to read and write to some packet SMD
 	  ports via device interface for MSM chipset.
 
+config MSM_ROTATOR
+        tristate "MSM Offline Image Rotator Driver"
+        depends on (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960) && ANDROID_PMEM
+        default y
+        help
+          This driver provides support for the image rotator HW block in the
+          MSM 7x30 SoC.
+
+config MSM_ROTATOR_USE_IMEM
+        bool "Enable rotator driver to use iMem"
+        depends on ARCH_MSM7X30 && MSM_ROTATOR
+        default y
+        help
+          This option enables the msm_rotator driver to use the move efficient
+          iMem.  Some MSM platforms may not have iMem available for the rotator
+          block.  Or some systems may want the iMem to be dedicated to a
+          different function.
+
+config MMC_GENERIC_CSDIO
+	tristate "Generic sdio driver"
+	default n
+	help
+	  SDIO function driver that extends SDIO card as character device
+	  in user space.
+
+config CSDIO_VENDOR_ID
+	hex "Card VendorId"
+	depends on MMC_GENERIC_CSDIO
+	default "0"
+	help
+	  Enter vendor id for targeted sdio device, this may be overwritten by
+	  module parameters.
+
+config CSDIO_DEVICE_ID
+	hex "CardDeviceId"
+	depends on MMC_GENERIC_CSDIO
+	default "0"
+	help
+	  Enter device id for targeted sdio device, this may be overwritten by
+	  module parameters.
+.
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 3f63254..1a295b8 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,7 +9,6 @@
 obj-$(CONFIG_VIRTIO_CONSOLE)	+= virtio_console.o
 obj-$(CONFIG_RAW_DRIVER)	+= raw.o
 obj-$(CONFIG_SGI_SNSC)		+= snsc.o snsc_event.o
-obj-$(CONFIG_MSM_SMD_PKT)	+= msm_smd_pkt.o
 obj-$(CONFIG_MSPEC)		+= mspec.o
 obj-$(CONFIG_MMTIMER)		+= mmtimer.o
 obj-$(CONFIG_UV_MMTIMER)	+= uv_mmtimer.o
@@ -64,3 +63,6 @@
 
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
 js-rtc-y = rtc.o
+
+obj-$(CONFIG_MSM_ROTATOR)	+= msm_rotator.o
+obj-$(CONFIG_MMC_GENERIC_CSDIO)	+= csdio.o
diff --git a/drivers/char/csdio.c b/drivers/char/csdio.c
new file mode 100644
index 0000000..ca7e986
--- /dev/null
+++ b/drivers/char/csdio.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2010, 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/gfp.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+/* Char device */
+#include <linux/cdev.h>
+#include <linux/fs.h>
+
+/* Sdio device */
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include <linux/csdio.h>
+
+#define FALSE   0
+#define TRUE    1
+
+#define VERSION                     "0.5"
+#define CSDIO_NUM_OF_SDIO_FUNCTIONS 7
+#define CSDIO_DEV_NAME              "csdio"
+#define TP_DEV_NAME                 CSDIO_DEV_NAME"f"
+#define CSDIO_DEV_PERMISSIONS       0666
+
+#define CSDIO_SDIO_BUFFER_SIZE      (64*512)
+
+int csdio_major;
+int csdio_minor;
+int csdio_transport_nr_devs = CSDIO_NUM_OF_SDIO_FUNCTIONS;
+static uint csdio_vendor_id;
+static uint csdio_device_id;
+static char *host_name;
+
+static struct csdio_func_t {
+	struct sdio_func   *m_func;
+	int                 m_enabled;
+	struct cdev         m_cdev;      /* char device structure */
+	struct device      *m_device;
+	u32                 m_block_size;
+} *g_csdio_func_table[CSDIO_NUM_OF_SDIO_FUNCTIONS] = {0};
+
+struct csdio_t {
+	struct cdev             m_cdev;
+	struct device          *m_device;
+	struct class           *m_driver_class;
+	struct fasync_struct   *m_async_queue;
+	unsigned char           m_current_irq_mask; /* currently enabled irqs */
+	struct mmc_host        *m_host;
+	unsigned int            m_num_of_func;
+} g_csdio;
+
+struct csdio_file_descriptor {
+	struct csdio_func_t    *m_port;
+	u32                     m_block_mode;/* data tran. byte(0)/block(1) */
+	u32                     m_op_code;   /* address auto increment flag */
+	u32                     m_address;
+};
+
+static void *g_sdio_buffer;
+
+/*
+ * Open and release
+ */
+static int csdio_transport_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_func_t *port = NULL; /*  device information */
+	struct sdio_func *func = NULL;
+	struct csdio_file_descriptor *descriptor = NULL;
+
+	port = container_of(inode->i_cdev, struct csdio_func_t, m_cdev);
+	func = port->m_func;
+	descriptor = kzalloc(sizeof(struct csdio_file_descriptor), GFP_KERNEL);
+	if (!descriptor) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	pr_info(TP_DEV_NAME"%d: open: func=%p, port=%p\n",
+			func->num, func, port);
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		pr_err(TP_DEV_NAME"%d:Enable func failed (%d)\n",
+				func->num, ret);
+		ret = -EIO;
+		goto free_descriptor;
+	}
+	descriptor->m_port = port;
+	filp->private_data = descriptor;
+	goto release_host;
+
+free_descriptor:
+	kfree(descriptor);
+release_host:
+	sdio_release_host(func);
+exit:
+	return ret;
+}
+
+static int csdio_transport_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+
+	pr_info(TP_DEV_NAME"%d: release\n", func->num);
+	sdio_claim_host(func);
+	ret = sdio_disable_func(func);
+	if (ret) {
+		pr_err(TP_DEV_NAME"%d:Disable func failed(%d)\n",
+				func->num, ret);
+		ret = -EIO;
+	}
+	sdio_release_host(func);
+	kfree(descriptor);
+	return ret;
+}
+
+/*
+ * Data management: read and write
+ */
+static ssize_t csdio_transport_read(struct file *filp,
+		char __user *buf,
+		size_t count,
+		loff_t *f_pos)
+{
+	ssize_t ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+	size_t t_count = count;
+
+	if (descriptor->m_block_mode) {
+		pr_info(TP_DEV_NAME "%d: CMD53 read, Md:%d, Addr:0x%04X,"
+				" Un:%d (Bl:%d, BlSz:%d)\n", func->num,
+				descriptor->m_block_mode,
+				descriptor->m_address,
+				count*port->m_block_size,
+				count, port->m_block_size);
+		/* recalculate size */
+		count *= port->m_block_size;
+	}
+	sdio_claim_host(func);
+	if (descriptor->m_op_code) {
+		/* auto increment */
+		ret = sdio_memcpy_fromio(func, g_sdio_buffer,
+				descriptor->m_address, count);
+	} else { /* FIFO */
+		ret = sdio_readsb(func, g_sdio_buffer,
+				descriptor->m_address, count);
+	}
+	sdio_release_host(func);
+	if (!ret) {
+		if (copy_to_user(buf, g_sdio_buffer, count))
+			ret = -EFAULT;
+		else
+			ret = t_count;
+	}
+	if (ret < 0) {
+		pr_err(TP_DEV_NAME "%d: CMD53 read failed (%d)"
+				"(Md:%d, Addr:0x%04X, Sz:%d)\n",
+				func->num, ret,
+				descriptor->m_block_mode,
+				descriptor->m_address, count);
+	}
+	return ret;
+}
+
+static ssize_t csdio_transport_write(struct file *filp,
+		const char __user *buf,
+		size_t count,
+		loff_t *f_pos)
+{
+	ssize_t ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+	size_t t_count = count;
+
+	if (descriptor->m_block_mode)
+		count *= port->m_block_size;
+
+	if (copy_from_user(g_sdio_buffer, buf, count)) {
+		pr_err(TP_DEV_NAME"%d:copy_from_user failed\n", func->num);
+		ret = -EFAULT;
+	} else {
+		sdio_claim_host(func);
+		if (descriptor->m_op_code) {
+			/* auto increment */
+			ret = sdio_memcpy_toio(func, descriptor->m_address,
+					g_sdio_buffer, count);
+		} else {
+			/* FIFO */
+			ret = sdio_writesb(func, descriptor->m_address,
+					g_sdio_buffer, count);
+		}
+		sdio_release_host(func);
+		if (!ret) {
+			ret = t_count;
+		} else {
+			pr_err(TP_DEV_NAME "%d: CMD53 write failed (%d)"
+				"(Md:%d, Addr:0x%04X, Sz:%d)\n",
+				func->num, ret, descriptor->m_block_mode,
+				descriptor->m_address, count);
+		}
+	}
+	return ret;
+}
+
+/* disable interrupt for sdio client */
+static int disable_sdio_client_isr(struct sdio_func *func)
+{
+	int ret;
+
+	/* disable for all functions, to restore interrupts
+	 * use g_csdio.m_current_irq_mask */
+	sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &ret);
+	if (ret)
+		pr_err(CSDIO_DEV_NAME" Can't sdio_f0_writeb (%d)\n", ret);
+
+	return ret;
+}
+
+/*
+ * This handles the interrupt from SDIO.
+ */
+static void csdio_sdio_irq(struct sdio_func *func)
+{
+	int ret;
+
+	pr_info(CSDIO_DEV_NAME" csdio_sdio_irq: func=%d\n", func->num);
+	ret = disable_sdio_client_isr(func);
+	if (ret) {
+		pr_err(CSDIO_DEV_NAME" Can't disable client isr(%d)\n", ret);
+		return;
+	}
+	/*  signal asynchronous readers */
+	if (g_csdio.m_async_queue)
+		kill_fasync(&g_csdio.m_async_queue, SIGIO, POLL_IN);
+}
+
+/*
+ * The ioctl() implementation
+ */
+static int csdio_transport_ioctl(struct inode *inode,
+		struct file *filp,
+		unsigned int cmd,
+		unsigned long arg)
+{
+	int err = 0;
+	int ret = 0;
+	struct csdio_file_descriptor *descriptor = filp->private_data;
+	struct csdio_func_t *port = descriptor->m_port;
+	struct sdio_func *func = port->m_func;
+
+	/*  extract the type and number bitfields
+	    sanity check: return ENOTTY (inappropriate ioctl) before
+	    access_ok()
+	*/
+	if ((_IOC_TYPE(cmd) != CSDIO_IOC_MAGIC) ||
+			(_IOC_NR(cmd) > CSDIO_IOC_MAXNR)) {
+		pr_err(TP_DEV_NAME "Wrong ioctl command parameters\n");
+		ret = -ENOTTY;
+		goto exit;
+	}
+
+	/*  the direction is a bitmask, and VERIFY_WRITE catches R/W
+	 *  transfers. `Type' is user-oriented, while access_ok is
+	    kernel-oriented, so the concept of "read" and "write" is reversed
+	*/
+	if (_IOC_DIR(cmd) & _IOC_READ) {
+		err = !access_ok(VERIFY_WRITE, (void __user *)arg,
+				_IOC_SIZE(cmd));
+	} else {
+		if (_IOC_DIR(cmd) & _IOC_WRITE) {
+			err =  !access_ok(VERIFY_READ, (void __user *)arg,
+					_IOC_SIZE(cmd));
+		}
+	}
+	if (err) {
+		pr_err(TP_DEV_NAME "Wrong ioctl access direction\n");
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	switch (cmd) {
+	case CSDIO_IOC_SET_OP_CODE:
+		{
+			pr_info(TP_DEV_NAME"%d:SET_OP_CODE=%d\n",
+					func->num, descriptor->m_op_code);
+			ret = get_user(descriptor->m_op_code,
+					(unsigned char __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_OP_CODE get data"
+						" from user space failed(%d)\n",
+						func->num, ret);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_FUNCTION_SET_BLOCK_SIZE:
+		{
+			unsigned block_size;
+
+			ret = get_user(block_size, (unsigned __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_SIZE get data"
+						" from user space failed(%d)\n",
+						func->num, ret);
+				ret = -ENOTTY;
+				break;
+			}
+			pr_info(TP_DEV_NAME"%d:SET_BLOCK_SIZE=%d\n",
+					func->num, block_size);
+			sdio_claim_host(func);
+			ret = sdio_set_block_size(func, block_size);
+			if (!ret) {
+				port->m_block_size = block_size;
+			} else {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_SIZE set block"
+						" size to %d failed (%d)\n",
+						func->num, block_size, ret);
+				ret = -ENOTTY;
+				break;
+			}
+			sdio_release_host(func);
+		}
+		break;
+	case CSDIO_IOC_SET_BLOCK_MODE:
+		{
+			pr_info(TP_DEV_NAME"%d:SET_BLOCK_MODE=%d\n",
+					func->num, descriptor->m_block_mode);
+			ret = get_user(descriptor->m_block_mode,
+					(unsigned char __user *)arg);
+			if (ret) {
+				pr_err(TP_DEV_NAME"%d:SET_BLOCK_MODE get data"
+						" from user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_CMD52:
+		{
+			struct csdio_cmd52_ctrl_t cmd52ctrl;
+			int cmd52ret;
+
+			if (copy_from_user(&cmd52ctrl,
+					(const unsigned char __user *)arg,
+					sizeof(cmd52ctrl))) {
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 get data"
+						" from user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+			sdio_claim_host(func);
+			if (cmd52ctrl.m_write)
+				sdio_writeb(func, cmd52ctrl.m_data,
+						cmd52ctrl.m_address, &cmd52ret);
+			else
+				cmd52ctrl.m_data = sdio_readb(func,
+						cmd52ctrl.m_address, &cmd52ret);
+
+			cmd52ctrl.m_ret = cmd52ret;
+			sdio_release_host(func);
+			if (cmd52ctrl.m_ret)
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 failed (%d)\n",
+						func->num, cmd52ctrl.m_ret);
+
+			if (copy_to_user((unsigned char __user *)arg,
+						&cmd52ctrl,
+						sizeof(cmd52ctrl))) {
+				pr_err(TP_DEV_NAME"%d:IOC_CMD52 put data"
+						" to user space failed\n",
+						func->num);
+				ret = -ENOTTY;
+				break;
+			}
+		}
+		break;
+	case CSDIO_IOC_CMD53:
+		{
+			struct csdio_cmd53_ctrl_t csdio_cmd53_ctrl;
+
+			if (copy_from_user(&csdio_cmd53_ctrl,
+						(const char __user *)arg,
+						sizeof(csdio_cmd53_ctrl))) {
+				ret = -EPERM;
+				pr_err(TP_DEV_NAME"%d:"
+					"Get data from user space failed\n",
+					func->num);
+				break;
+			}
+			descriptor->m_block_mode =
+				csdio_cmd53_ctrl.m_block_mode;
+			descriptor->m_op_code = csdio_cmd53_ctrl.m_op_code;
+			descriptor->m_address = csdio_cmd53_ctrl.m_address;
+		}
+		break;
+	case CSDIO_IOC_CONNECT_ISR:
+		{
+			pr_info(CSDIO_DEV_NAME" SDIO_CONNECT_ISR"
+				" func=%d, csdio_sdio_irq=%x\n",
+				func->num, (unsigned int)csdio_sdio_irq);
+			sdio_claim_host(func);
+			ret = sdio_claim_irq(func, csdio_sdio_irq);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME" SDIO_CONNECT_ISR"
+						" claim irq failed(%d)\n", ret);
+			} else {
+				/* update current irq mask for disable/enable */
+				g_csdio.m_current_irq_mask |= (1 << func->num);
+			}
+		}
+		break;
+	case CSDIO_IOC_DISCONNECT_ISR:
+		{
+			pr_info(CSDIO_DEV_NAME " SDIO_DISCONNECT_ISR func=%d\n",
+					func->num);
+			sdio_claim_host(func);
+			sdio_release_irq(func);
+			sdio_release_host(func);
+			/* update current irq mask for disable/enable */
+			g_csdio.m_current_irq_mask &= ~(1 << func->num);
+		}
+		break;
+	default:  /*  redundant, as cmd was checked against MAXNR */
+		pr_warning(TP_DEV_NAME"%d: Redundant IOCTL\n",
+				func->num);
+		ret = -ENOTTY;
+	}
+exit:
+	return ret;
+}
+
+static const struct file_operations csdio_transport_fops = {
+	.owner =    THIS_MODULE,
+	.read =     csdio_transport_read,
+	.write =    csdio_transport_write,
+	.ioctl =    csdio_transport_ioctl,
+	.open =     csdio_transport_open,
+	.release =  csdio_transport_release,
+};
+
+static void csdio_transport_cleanup(struct csdio_func_t *port)
+{
+	int devno = MKDEV(csdio_major, csdio_minor + port->m_func->num);
+	device_destroy(g_csdio.m_driver_class, devno);
+	port->m_device = NULL;
+	cdev_del(&port->m_cdev);
+}
+
+#if defined(CONFIG_DEVTMPFS)
+static inline int csdio_cdev_update_permissions(
+    const char *devname, int dev_minor)
+{
+	return 0;
+}
+#else
+static int csdio_cdev_update_permissions(
+    const char *devname, int dev_minor)
+{
+	int ret = 0;
+	mm_segment_t fs;
+	struct file *file;
+	struct inode *inode;
+	struct iattr newattrs;
+	int mode = CSDIO_DEV_PERMISSIONS;
+	char dev_file[64];
+
+	fs = get_fs();
+	set_fs(get_ds());
+
+	snprintf(dev_file, sizeof(dev_file), "/dev/%s%d",
+		devname, dev_minor);
+	file = filp_open(dev_file, O_RDWR, 0);
+	if (IS_ERR(file)) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	inode = file->f_path.dentry->d_inode;
+
+	mutex_lock(&inode->i_mutex);
+	newattrs.ia_mode =
+		(mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	ret = notify_change(file->f_path.dentry, &newattrs);
+	mutex_unlock(&inode->i_mutex);
+
+	filp_close(file, NULL);
+
+exit:
+	set_fs(fs);
+	return ret;
+}
+#endif
+
+static struct device *csdio_cdev_init(struct cdev *char_dev,
+		const struct file_operations *file_op, int dev_minor,
+		const char *devname, struct device *parent)
+{
+	int ret = 0;
+	struct device *new_device = NULL;
+	dev_t devno = MKDEV(csdio_major, dev_minor);
+
+	/*  Initialize transport device */
+	cdev_init(char_dev, file_op);
+	char_dev->owner = THIS_MODULE;
+	char_dev->ops = file_op;
+	ret = cdev_add(char_dev, devno, 1);
+
+	/*  Fail gracefully if need be */
+	if (ret) {
+		pr_warning("Error %d adding CSDIO char device '%s%d'",
+				ret, devname, dev_minor);
+		goto exit;
+	}
+	pr_info("'%s%d' char driver registered\n", devname, dev_minor);
+
+	/*  create a /dev entry for transport drivers */
+	new_device = device_create(g_csdio.m_driver_class, parent, devno, NULL,
+			"%s%d", devname, dev_minor);
+	if (!new_device) {
+		pr_err("Can't create device node '/dev/%s%d'\n",
+				devname, dev_minor);
+		goto cleanup;
+	}
+	/* no irq attached */
+	g_csdio.m_current_irq_mask = 0;
+
+	if (csdio_cdev_update_permissions(devname, dev_minor)) {
+		pr_warning("%s%d: Unable to update access permissions of the"
+			" '/dev/%s%d'\n",
+			devname, dev_minor, devname, dev_minor);
+	}
+
+	pr_info("%s%d: Device node '/dev/%s%d' created successfully\n",
+			devname, dev_minor, devname, dev_minor);
+	goto exit;
+cleanup:
+	cdev_del(char_dev);
+exit:
+	return new_device;
+}
+
+/* Looks for first non empty function, returns NULL otherwise */
+static struct sdio_func *get_active_func(void)
+{
+	int i;
+
+	for (i = 0; i < CSDIO_NUM_OF_SDIO_FUNCTIONS; i++) {
+		if (g_csdio_func_table[i])
+			return g_csdio_func_table[i]->m_func;
+	}
+	return NULL;
+}
+
+static ssize_t
+show_vdd(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	if (NULL == g_csdio.m_host)
+		return snprintf(buf, PAGE_SIZE, "N/A\n");
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		g_csdio.m_host->ios.vdd);
+}
+
+static int
+set_vdd_helper(int value)
+{
+	struct mmc_ios *ios = NULL;
+
+	if (NULL == g_csdio.m_host) {
+		pr_err("%s0: Set VDD, no MMC host assigned\n", CSDIO_DEV_NAME);
+		return -ENXIO;
+	}
+
+	mmc_claim_host(g_csdio.m_host);
+	ios = &g_csdio.m_host->ios;
+	ios->vdd = value;
+	g_csdio.m_host->ops->set_ios(g_csdio.m_host, ios);
+	mmc_release_host(g_csdio.m_host);
+	return 0;
+}
+
+static ssize_t
+set_vdd(struct device *dev, struct device_attribute *att,
+	const char *buf, size_t count)
+{
+	int value = 0;
+
+	sscanf(buf, "%d", &value);
+	if (set_vdd_helper(value))
+		return -ENXIO;
+	return count;
+}
+
+static DEVICE_ATTR(vdd, S_IRUGO | S_IWUSR,
+	show_vdd, set_vdd);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_vdd.attr,
+	NULL,
+};
+
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+
+/*
+ * The ioctl() implementation for control device
+ */
+static int csdio_ctrl_ioctl(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	int ret = 0;
+
+	pr_info("CSDIO ctrl ioctl.\n");
+
+	/*  extract the type and number bitfields
+	    sanity check: return ENOTTY (inappropriate ioctl) before
+	    access_ok()
+	*/
+	if ((_IOC_TYPE(cmd) != CSDIO_IOC_MAGIC) ||
+			(_IOC_NR(cmd) > CSDIO_IOC_MAXNR)) {
+		pr_err(CSDIO_DEV_NAME "Wrong ioctl command parameters\n");
+		ret = -ENOTTY;
+		goto exit;
+	}
+
+	/*  the direction is a bitmask, and VERIFY_WRITE catches R/W
+	  transfers. `Type' is user-oriented, while access_ok is
+	  kernel-oriented, so the concept of "read" and "write" is reversed
+	  */
+	if (_IOC_DIR(cmd) & _IOC_READ) {
+		err = !access_ok(VERIFY_WRITE, (void __user *)arg,
+				_IOC_SIZE(cmd));
+	} else {
+		if (_IOC_DIR(cmd) & _IOC_WRITE)
+			err =  !access_ok(VERIFY_READ, (void __user *)arg,
+					_IOC_SIZE(cmd));
+	}
+	if (err) {
+		pr_err(CSDIO_DEV_NAME "Wrong ioctl access direction\n");
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	switch (cmd) {
+	case CSDIO_IOC_ENABLE_HIGHSPEED_MODE:
+		pr_info(CSDIO_DEV_NAME" ENABLE_HIGHSPEED_MODE\n");
+		break;
+	case CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS:
+		{
+			struct mmc_host *host = g_csdio.m_host;
+			struct mmc_ios *ios = NULL;
+
+			if (NULL == host) {
+				pr_err("%s0: "
+					"CSDIO_IOC_SET_DATA_TRANSFER_CLOCKS,"
+					" no MMC host assigned\n",
+					CSDIO_DEV_NAME);
+				ret = -EFAULT;
+				goto exit;
+			}
+			ios = &host->ios;
+
+			mmc_claim_host(host);
+			ret = get_user(host->ios.clock,
+					(unsigned int __user *)arg);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME
+					" get data from user space failed\n");
+			} else {
+				pr_err(CSDIO_DEV_NAME
+					"SET_DATA_TRANSFER_CLOCKS(%d-%d)(%d)\n",
+					host->f_min, host->f_max,
+					host->ios.clock);
+				host->ops->set_ios(host, ios);
+			}
+			mmc_release_host(host);
+		}
+		break;
+	case CSDIO_IOC_ENABLE_ISR:
+		{
+			int ret;
+			unsigned char reg;
+			struct sdio_func *func = get_active_func();
+
+			if (!func) {
+				pr_err(CSDIO_DEV_NAME " CSDIO_IOC_ENABLE_ISR"
+						" no active sdio function\n");
+				ret = -EFAULT;
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME
+					" CSDIO_IOC_ENABLE_ISR func=%d\n",
+					func->num);
+			reg = g_csdio.m_current_irq_mask | 1;
+
+			sdio_claim_host(func);
+			sdio_f0_writeb(func, reg, SDIO_CCCR_IENx, &ret);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err(CSDIO_DEV_NAME
+						" Can't sdio_f0_writeb (%d)\n",
+						ret);
+				goto exit;
+			}
+		}
+		break;
+	case CSDIO_IOC_DISABLE_ISR:
+		{
+			int ret;
+			struct sdio_func *func = get_active_func();
+			if (!func) {
+				pr_err(CSDIO_DEV_NAME " CSDIO_IOC_ENABLE_ISR"
+						" no active sdio function\n");
+				ret = -EFAULT;
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME
+					" CSDIO_IOC_DISABLE_ISR func=%p\n",
+					func);
+
+			sdio_claim_host(func);
+			ret = disable_sdio_client_isr(func);
+			sdio_release_host(func);
+			if (ret) {
+				pr_err("%s0: Can't disable client isr (%d)\n",
+					CSDIO_DEV_NAME, ret);
+				goto exit;
+			}
+		}
+	break;
+	case CSDIO_IOC_SET_VDD:
+		{
+			unsigned int vdd = 0;
+
+			ret = get_user(vdd, (unsigned int __user *)arg);
+			if (ret) {
+				pr_err("%s0: CSDIO_IOC_SET_VDD,"
+					" get data from user space failed\n",
+					CSDIO_DEV_NAME);
+				goto exit;
+			}
+			pr_info(CSDIO_DEV_NAME" CSDIO_IOC_SET_VDD - %d\n", vdd);
+
+			ret = set_vdd_helper(vdd);
+			if (ret)
+				goto exit;
+		}
+	break;
+	case CSDIO_IOC_GET_VDD:
+		{
+			if (NULL == g_csdio.m_host) {
+				pr_err("%s0: CSDIO_IOC_GET_VDD,"
+					" no MMC host assigned\n",
+					CSDIO_DEV_NAME);
+				ret = -EFAULT;
+				goto exit;
+			}
+			ret = put_user(g_csdio.m_host->ios.vdd,
+				(unsigned short __user *)arg);
+			if (ret) {
+				pr_err("%s0: CSDIO_IOC_GET_VDD, put data"
+					" to user space failed\n",
+					CSDIO_DEV_NAME);
+				goto exit;
+			}
+		}
+	break;
+	default:  /*  redundant, as cmd was checked against MAXNR */
+		pr_warning(CSDIO_DEV_NAME" Redundant IOCTL\n");
+		ret = -ENOTTY;
+	}
+exit:
+	return ret;
+}
+
+static int csdio_ctrl_fasync(int fd, struct file *filp, int mode)
+{
+	pr_info(CSDIO_DEV_NAME
+			" csdio_ctrl_fasync: fd=%d, filp=%p, mode=%d\n",
+			fd, filp, mode);
+	return fasync_helper(fd, filp, mode, &g_csdio.m_async_queue);
+}
+
+/*
+ * Open and close
+ */
+static int csdio_ctrl_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct csdio_t *csdio_ctrl_drv = NULL; /*  device information */
+
+	pr_info("CSDIO ctrl open.\n");
+	csdio_ctrl_drv = container_of(inode->i_cdev, struct csdio_t, m_cdev);
+	filp->private_data = csdio_ctrl_drv; /*  for other methods */
+	return ret;
+}
+
+static int csdio_ctrl_release(struct inode *inode, struct file *filp)
+{
+	pr_info("CSDIO ctrl release.\n");
+	/*  remove this filp from the asynchronously notified filp's */
+	csdio_ctrl_fasync(-1, filp, 0);
+	return 0;
+}
+
+static const struct file_operations csdio_ctrl_fops = {
+	.owner =	THIS_MODULE,
+	.ioctl =	csdio_ctrl_ioctl,
+	.open  =	csdio_ctrl_open,
+	.release =	csdio_ctrl_release,
+	.fasync =	csdio_ctrl_fasync,
+};
+
+static int csdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *id)
+{
+	struct csdio_func_t *port;
+	int ret = 0;
+	struct mmc_host *host = func->card->host;
+
+	if (NULL != g_csdio.m_host && g_csdio.m_host != host) {
+		pr_info("%s: Device is on unexpected host\n",
+			CSDIO_DEV_NAME);
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	/* enforce single instance policy */
+	if (g_csdio_func_table[func->num-1]) {
+		pr_err("%s - only single SDIO device supported",
+				sdio_func_id(func));
+		ret = -EEXIST;
+		goto exit;
+	}
+
+	port = kzalloc(sizeof(struct csdio_func_t), GFP_KERNEL);
+	if (!port) {
+		pr_err("Can't allocate memory\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	/* initialize SDIO side */
+	port->m_func = func;
+	sdio_set_drvdata(func, port);
+
+	pr_info("%s - SDIO device found. Function %d\n",
+			sdio_func_id(func), func->num);
+
+	port->m_device = csdio_cdev_init(&port->m_cdev, &csdio_transport_fops,
+			csdio_minor + port->m_func->num,
+			TP_DEV_NAME, &port->m_func->dev);
+
+	/* create appropriate char device */
+	if (!port->m_device)
+		goto free;
+
+	if (0 == g_csdio.m_num_of_func && NULL == host_name)
+		g_csdio.m_host = host;
+	g_csdio.m_num_of_func++;
+	g_csdio_func_table[func->num-1] = port;
+	port->m_enabled = TRUE;
+	goto exit;
+free:
+	kfree(port);
+exit:
+	return ret;
+}
+
+static void csdio_remove(struct sdio_func *func)
+{
+	struct csdio_func_t *port = sdio_get_drvdata(func);
+
+	csdio_transport_cleanup(port);
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+	kfree(port);
+	g_csdio_func_table[func->num-1] = NULL;
+	g_csdio.m_num_of_func--;
+	if (0 == g_csdio.m_num_of_func && NULL == host_name)
+		g_csdio.m_host = NULL;
+	pr_info("%s%d: Device removed (%s). Function %d\n",
+		CSDIO_DEV_NAME, func->num, sdio_func_id(func), func->num);
+}
+
+/* CONFIG_CSDIO_VENDOR_ID and CONFIG_CSDIO_DEVICE_ID are defined in Kconfig.
+ * Use kernel configuration to change the values or overwrite them through
+ * module parameters */
+static struct sdio_device_id csdio_ids[] = {
+	{ SDIO_DEVICE(CONFIG_CSDIO_VENDOR_ID, CONFIG_CSDIO_DEVICE_ID) },
+	{ /* end: all zeroes */},
+};
+
+MODULE_DEVICE_TABLE(sdio, csdio_ids);
+
+static struct sdio_driver csdio_driver = {
+	.probe      = csdio_probe,
+	.remove     = csdio_remove,
+	.name       = "csdio",
+	.id_table   = csdio_ids,
+};
+
+static void __exit csdio_exit(void)
+{
+	dev_t devno = MKDEV(csdio_major, csdio_minor);
+
+	sdio_unregister_driver(&csdio_driver);
+	sysfs_remove_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+	kfree(g_sdio_buffer);
+	device_destroy(g_csdio.m_driver_class, devno);
+	cdev_del(&g_csdio.m_cdev);
+	class_destroy(g_csdio.m_driver_class);
+	unregister_chrdev_region(devno, csdio_transport_nr_devs);
+	pr_info("%s: Exit driver module\n", CSDIO_DEV_NAME);
+}
+
+static char *csdio_devnode(struct device *dev, mode_t *mode)
+{
+	*mode = CSDIO_DEV_PERMISSIONS;
+	return NULL;
+}
+
+static int __init csdio_init(void)
+{
+	int ret = 0;
+	dev_t devno = 0;
+
+	pr_info("Init CSDIO driver module.\n");
+
+	/*  Get a range of minor numbers to work with, asking for a dynamic */
+	/*  major unless directed otherwise at load time. */
+	if (csdio_major) {
+		devno = MKDEV(csdio_major, csdio_minor);
+		ret = register_chrdev_region(devno, csdio_transport_nr_devs,
+				CSDIO_DEV_NAME);
+	} else {
+		ret = alloc_chrdev_region(&devno, csdio_minor,
+				csdio_transport_nr_devs, CSDIO_DEV_NAME);
+		csdio_major = MAJOR(devno);
+	}
+	if (ret < 0) {
+		pr_err("CSDIO: can't get major %d\n", csdio_major);
+		goto exit;
+	}
+	pr_info("CSDIO char driver major number is %d\n", csdio_major);
+
+	/* kernel module got parameters: overwrite vendor and device id's */
+	if ((csdio_vendor_id != 0) && (csdio_device_id != 0)) {
+		csdio_ids[0].vendor = (u16)csdio_vendor_id;
+		csdio_ids[0].device = (u16)csdio_device_id;
+	}
+
+	/*  prepare create /dev/... instance */
+	g_csdio.m_driver_class = class_create(THIS_MODULE, CSDIO_DEV_NAME);
+	if (IS_ERR(g_csdio.m_driver_class)) {
+		ret = -ENOMEM;
+		pr_err(CSDIO_DEV_NAME " class_create failed\n");
+		goto unregister_region;
+	}
+	g_csdio.m_driver_class->devnode = csdio_devnode;
+
+	/*  create CSDIO ctrl driver */
+	g_csdio.m_device = csdio_cdev_init(&g_csdio.m_cdev,
+		&csdio_ctrl_fops, csdio_minor, CSDIO_DEV_NAME, NULL);
+	if (!g_csdio.m_device) {
+		pr_err("%s: Unable to create ctrl driver\n",
+			CSDIO_DEV_NAME);
+		goto destroy_class;
+	}
+
+	g_sdio_buffer = kmalloc(CSDIO_SDIO_BUFFER_SIZE, GFP_KERNEL);
+	if (!g_sdio_buffer) {
+		pr_err("Unable to allocate %d bytes\n", CSDIO_SDIO_BUFFER_SIZE);
+		ret = -ENOMEM;
+		goto destroy_cdev;
+	}
+
+	ret = sysfs_create_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+	if (ret) {
+		pr_err("%s: Unable to create device attribute\n",
+			CSDIO_DEV_NAME);
+		goto free_sdio_buff;
+	}
+
+	g_csdio.m_num_of_func = 0;
+	g_csdio.m_host = NULL;
+
+	if (NULL != host_name) {
+		struct device *dev = bus_find_device_by_name(&platform_bus_type,
+			NULL, host_name);
+		if (NULL != dev) {
+			g_csdio.m_host = dev_get_drvdata(dev);
+		} else {
+			pr_err("%s: Host '%s' doesn't exist!\n", CSDIO_DEV_NAME,
+				host_name);
+		}
+	}
+
+	pr_info("%s: Match with VendorId=0x%X, DeviceId=0x%X, Host = %s\n",
+		CSDIO_DEV_NAME, csdio_device_id, csdio_vendor_id,
+		(NULL == host_name) ? "Any" : host_name);
+
+	/* register sdio driver */
+	ret = sdio_register_driver(&csdio_driver);
+	if (ret) {
+		pr_err("%s: Unable to register as SDIO driver\n",
+			CSDIO_DEV_NAME);
+		goto remove_group;
+	}
+
+	goto exit;
+
+remove_group:
+	sysfs_remove_group(&g_csdio.m_device->kobj, &dev_attr_grp);
+free_sdio_buff:
+	kfree(g_sdio_buffer);
+destroy_cdev:
+	cdev_del(&g_csdio.m_cdev);
+destroy_class:
+	class_destroy(g_csdio.m_driver_class);
+unregister_region:
+	unregister_chrdev_region(devno, csdio_transport_nr_devs);
+exit:
+	return ret;
+}
+module_param(csdio_vendor_id, uint, S_IRUGO);
+module_param(csdio_device_id, uint, S_IRUGO);
+module_param(host_name, charp, S_IRUGO);
+
+module_init(csdio_init);
+module_exit(csdio_exit);
+
+MODULE_AUTHOR("Code Aurora Forum");
+MODULE_DESCRIPTION("CSDIO device driver version " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
index a787acc..7643f50 100644
--- a/drivers/char/dcc_tty.c
+++ b/drivers/char/dcc_tty.c
@@ -21,12 +21,13 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/spinlock.h>
 
 MODULE_DESCRIPTION("DCC TTY Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.0");
 
-static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t g_dcc_tty_lock = __SPIN_LOCK_UNLOCKED(g_dcc_tty_lock);
 static struct hrtimer g_dcc_timer;
 static char g_dcc_buffer[16];
 static int g_dcc_buffer_head;
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
new file mode 100644
index 0000000..eb0b21e
--- /dev/null
+++ b/drivers/char/diag/Kconfig
@@ -0,0 +1,31 @@
+menu "Diag Support"
+
+config DIAG_CHAR
+	tristate "char driver interface and diag forwarding to/from modem"
+	default m
+	depends on USB_G_ANDROID || USB_FUNCTION_DIAG || USB_QCOM_MAEMO
+	depends on ARCH_MSM
+	help
+	 Char driver interface for diag user space and diag-forwarding to modem ARM and back.
+	 This enables diagchar for maemo usb gadget or android usb gadget based on config selected.
+endmenu
+
+menu "DIAG traffic over USB"
+
+config DIAG_OVER_USB
+	bool "Enable DIAG traffic to go over USB"
+        depends on ARCH_MSM
+	default y
+	help
+	 This feature helps segregate code required for DIAG traffic to go over USB.
+endmenu
+
+menu "SDIO support for DIAG"
+
+config DIAG_SDIO_PIPE
+	depends on MSM_SDIO_AL
+	default y
+	bool "Enable 9K DIAG traffic over SDIO"
+	help
+	 SDIO Transport Layer for DIAG Router
+endmenu
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
new file mode 100644
index 0000000..52ab2b9
--- /dev/null
+++ b/drivers/char/diag/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DIAG_CHAR) := diagchar.o
+obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
+diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
new file mode 100644
index 0000000..6041954
--- /dev/null
+++ b/drivers/char/diag/diagchar.h
@@ -0,0 +1,222 @@
+/* Copyright (c) 2008-2011, 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.
+ */
+
+#ifndef DIAGCHAR_H
+#define DIAGCHAR_H
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <mach/msm_smd.h>
+#include <asm/atomic.h>
+#include <asm/mach-types.h>
+/* Size of the USB buffers used for read and write*/
+#define USB_MAX_OUT_BUF 4096
+#define IN_BUF_SIZE		16384
+#define MAX_IN_BUF_SIZE	32768
+#define MAX_SYNC_OBJ_NAME_SIZE	32
+/* Size of the buffer used for deframing a packet
+  reveived from the PC tool*/
+#define HDLC_MAX 4096
+#define HDLC_OUT_BUF_SIZE	8192
+#define POOL_TYPE_COPY		1
+#define POOL_TYPE_HDLC		2
+#define POOL_TYPE_WRITE_STRUCT	4
+#define POOL_TYPE_ALL		7
+#define MODEM_DATA 		1
+#define QDSP_DATA  		2
+#define APPS_DATA  		3
+#define SDIO_DATA		4
+#define WCNSS_DATA		5
+#define MODEM_PROC		0
+#define APPS_PROC		1
+#define QDSP_PROC		2
+#define WCNSS_PROC		3
+#define MSG_MASK_SIZE 8000
+#define LOG_MASK_SIZE 8000
+#define EVENT_MASK_SIZE 1000
+#define PKT_SIZE 4096
+#define MAX_EQUIP_ID 12
+
+/* Maximum number of pkt reg supported at initialization*/
+extern unsigned int diag_max_registration;
+extern unsigned int diag_threshold_registration;
+
+#define APPEND_DEBUG(ch) \
+do {							\
+	diag_debug_buf[diag_debug_buf_idx] = ch; \
+	(diag_debug_buf_idx < 1023) ? \
+	(diag_debug_buf_idx++) : (diag_debug_buf_idx = 0); \
+} while (0)
+
+struct diag_master_table {
+	uint16_t cmd_code;
+	uint16_t subsys_id;
+	uint32_t client_id;
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	int process_id;
+};
+
+struct bindpkt_params_per_process {
+	/* Name of the synchronization object associated with this proc */
+	char sync_obj_name[MAX_SYNC_OBJ_NAME_SIZE];
+	uint32_t count;	/* Number of entries in this bind */
+	struct bindpkt_params *params; /* first bind params */
+};
+
+struct bindpkt_params {
+	uint16_t cmd_code;
+	uint16_t subsys_id;
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	/* For Central Routing, used to store Processor number */
+	uint16_t proc_id;
+	uint32_t event_id;
+	uint32_t log_code;
+	/* For Central Routing, used to store SMD channel pointer */
+	uint32_t client_id;
+};
+
+struct diag_write_device {
+	void *buf;
+	int length;
+};
+
+struct diag_client_map {
+	char name[20];
+	int pid;
+};
+
+/* This structure is defined in USB header file */
+#ifndef CONFIG_DIAG_OVER_USB
+struct diag_request {
+	char *buf;
+	int length;
+	int actual;
+	int status;
+	void *context;
+};
+#endif
+
+struct diagchar_dev {
+
+	/* State for the char driver */
+	unsigned int major;
+	unsigned int minor_start;
+	int num;
+	struct cdev *cdev;
+	char *name;
+	int dropped_count;
+	struct class *diagchar_class;
+	int ref_count;
+	struct mutex diagchar_mutex;
+	wait_queue_head_t wait_q;
+	struct diag_client_map *client_map;
+	int *data_ready;
+	int num_clients;
+	struct diag_write_device *buf_tbl;
+
+	/* Memory pool parameters */
+	unsigned int itemsize;
+	unsigned int poolsize;
+	unsigned int itemsize_hdlc;
+	unsigned int poolsize_hdlc;
+	unsigned int itemsize_write_struct;
+	unsigned int poolsize_write_struct;
+	unsigned int debug_flag;
+	/* State for the mempool for the char driver */
+	mempool_t *diagpool;
+	mempool_t *diag_hdlc_pool;
+	mempool_t *diag_write_struct_pool;
+	struct mutex diagmem_mutex;
+	int count;
+	int count_hdlc_pool;
+	int count_write_struct_pool;
+	int used;
+
+	/* State for diag forwarding */
+	unsigned char *buf_in_1;
+	unsigned char *buf_in_2;
+	unsigned char *buf_in_cntl;
+	unsigned char *buf_in_qdsp_1;
+	unsigned char *buf_in_qdsp_2;
+	unsigned char *buf_in_qdsp_cntl;
+	unsigned char *buf_in_wcnss;
+	unsigned char *buf_in_wcnss_cntl;
+	unsigned char *usb_buf_out;
+	unsigned char *apps_rsp_buf;
+	smd_channel_t *ch;
+	smd_channel_t *ch_cntl;
+	smd_channel_t *chqdsp;
+	smd_channel_t *chqdsp_cntl;
+	smd_channel_t *ch_wcnss;
+	smd_channel_t *ch_wcnss_cntl;
+	int in_busy_1;
+	int in_busy_2;
+	int in_busy_qdsp_1;
+	int in_busy_qdsp_2;
+	int in_busy_wcnss;
+	int read_len_legacy;
+	unsigned char *hdlc_buf;
+	unsigned hdlc_count;
+	unsigned hdlc_escape;
+#ifdef CONFIG_DIAG_OVER_USB
+	int usb_connected;
+	struct usb_diag_ch *legacy_ch;
+	struct work_struct diag_proc_hdlc_work;
+	struct work_struct diag_read_work;
+#endif
+	struct workqueue_struct *diag_wq;
+	struct work_struct diag_drain_work;
+	struct work_struct diag_read_smd_work;
+	struct work_struct diag_read_smd_cntl_work;
+	struct work_struct diag_read_smd_qdsp_work;
+	struct work_struct diag_read_smd_qdsp_cntl_work;
+	struct work_struct diag_read_smd_wcnss_work;
+	struct work_struct diag_read_smd_wcnss_cntl_work;
+	uint8_t *msg_masks;
+	uint8_t *log_masks;
+	int log_masks_length;
+	uint8_t *event_masks;
+	struct diag_master_table *table;
+	uint8_t *pkt_buf;
+	int pkt_length;
+	struct diag_request *write_ptr_1;
+	struct diag_request *write_ptr_2;
+	struct diag_request *usb_read_ptr;
+	struct diag_request *write_ptr_svc;
+	struct diag_request *write_ptr_qdsp_1;
+	struct diag_request *write_ptr_qdsp_2;
+	struct diag_request *write_ptr_wcnss;
+	int logging_mode;
+	int logging_process_id;
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	unsigned char *buf_in_sdio;
+	unsigned char *usb_buf_mdm_out;
+	struct sdio_channel *sdio_ch;
+	int read_len_mdm;
+	int in_busy_sdio;
+	struct usb_diag_ch *mdm_ch;
+	struct work_struct diag_read_mdm_work;
+	struct workqueue_struct *diag_sdio_wq;
+	struct work_struct diag_read_sdio_work;
+	struct work_struct diag_remove_sdio_work;
+	struct diag_request *usb_read_mdm_ptr;
+	struct diag_request *write_ptr_mdm;
+#endif
+};
+
+extern struct diagchar_dev *driver;
+#endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
new file mode 100644
index 0000000..c9a9d57
--- /dev/null
+++ b/drivers/char/diag/diagchar_core.c
@@ -0,0 +1,982 @@
+/* Copyright (c) 2008-2011, 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/init.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include <asm/current.h>
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+#ifdef CONFIG_DIAG_SDIO_PIPE
+#include "diagfwd_sdio.h"
+#endif
+#include <linux/timer.h>
+
+MODULE_DESCRIPTION("Diag Char Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+
+#define INIT	1
+#define EXIT	-1
+struct diagchar_dev *driver;
+struct diagchar_priv {
+	int pid;
+};
+/* The following variables can be specified by module options */
+ /* for copy buffer */
+static unsigned int itemsize = 2048; /*Size of item in the mempool */
+static unsigned int poolsize = 10; /*Number of items in the mempool */
+/* for hdlc buffer */
+static unsigned int itemsize_hdlc = 8192; /*Size of item in the mempool */
+static unsigned int poolsize_hdlc = 8;  /*Number of items in the mempool */
+/* for write structure buffer */
+static unsigned int itemsize_write_struct = 20; /*Size of item in the mempool */
+static unsigned int poolsize_write_struct = 8; /* Num of items in the mempool */
+/* This is the max number of user-space clients supported at initialization*/
+static unsigned int max_clients = 15;
+static unsigned int threshold_client_limit = 30;
+/* This is the maximum number of pkt registrations supported at initialization*/
+unsigned int diag_max_registration = 500;
+unsigned int diag_threshold_registration = 650;
+
+/* Timer variables */
+static struct timer_list drain_timer;
+static int timer_in_progress;
+void *buf_hdlc;
+module_param(itemsize, uint, 0);
+module_param(poolsize, uint, 0);
+module_param(max_clients, uint, 0);
+
+/* delayed_rsp_id 0 represents no delay in the response. Any other number
+    means that the diag packet has a delayed response. */
+static uint16_t delayed_rsp_id = 1;
+#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
+/* This macro gets the next delayed respose id. Once it reaches
+ DIAGPKT_MAX_DELAYED_RSP, it stays at DIAGPKT_MAX_DELAYED_RSP */
+
+#define DIAGPKT_NEXT_DELAYED_RSP_ID(x) 				\
+((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
+
+#define COPY_USER_SPACE_OR_EXIT(buf, data, length)		\
+do {								\
+	if ((count < ret+length) || (copy_to_user(buf,		\
+			(void *)&data, length))) {		\
+		ret = -EFAULT;					\
+		goto exit;					\
+	}							\
+	ret += length;						\
+} while (0)
+
+static void drain_timer_func(unsigned long data)
+{
+	queue_work(driver->diag_wq , &(driver->diag_drain_work));
+}
+
+void diag_drain_work_fn(struct work_struct *work)
+{
+	int err = 0;
+	timer_in_progress = 0;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (buf_hdlc) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+		}
+		buf_hdlc = NULL;
+#ifdef DIAG_DEBUG
+		pr_debug("diag: Number of bytes written "
+				 "from timer is %d ", driver->used);
+#endif
+		driver->used = 0;
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_read_smd_work_fn(struct work_struct *work)
+{
+	__diag_smd_send_req();
+}
+
+void diag_read_smd_qdsp_work_fn(struct work_struct *work)
+{
+	__diag_smd_qdsp_send_req();
+}
+
+void diag_read_smd_wcnss_work_fn(struct work_struct *work)
+{
+	__diag_smd_wcnss_send_req();
+}
+
+void diag_add_client(int i, struct file *file)
+{
+	struct diagchar_priv *diagpriv_data;
+
+	driver->client_map[i].pid = current->tgid;
+	diagpriv_data = kmalloc(sizeof(struct diagchar_priv),
+							GFP_KERNEL);
+	if (diagpriv_data)
+		diagpriv_data->pid = current->tgid;
+	file->private_data = diagpriv_data;
+	strncpy(driver->client_map[i].name, current->comm, 20);
+	driver->client_map[i].name[19] = '\0';
+}
+
+static int diagchar_open(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	void *temp;
+
+	if (driver) {
+		mutex_lock(&driver->diagchar_mutex);
+
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid == 0)
+				break;
+
+		if (i < driver->num_clients) {
+			diag_add_client(i, file);
+		} else {
+			if (i < threshold_client_limit) {
+				driver->num_clients++;
+				temp = krealloc(driver->client_map
+					, (driver->num_clients) * sizeof(struct
+						 diag_client_map), GFP_KERNEL);
+				if (!temp)
+					goto fail;
+				else
+					driver->client_map = temp;
+				temp = krealloc(driver->data_ready
+					, (driver->num_clients) * sizeof(int),
+							GFP_KERNEL);
+				if (!temp)
+					goto fail;
+				else
+					driver->data_ready = temp;
+				diag_add_client(i, file);
+			} else {
+				mutex_unlock(&driver->diagchar_mutex);
+				pr_alert("Max client limit for DIAG reached\n");
+				pr_info("Cannot open handle %s"
+					   " %d", current->comm, current->tgid);
+				for (i = 0; i < driver->num_clients; i++)
+					pr_debug("%d) %s PID=%d", i, driver->
+						client_map[i].name,
+						driver->client_map[i].pid);
+				return -ENOMEM;
+			}
+		}
+		driver->data_ready[i] |= MSG_MASKS_TYPE;
+		driver->data_ready[i] |= EVENT_MASKS_TYPE;
+		driver->data_ready[i] |= LOG_MASKS_TYPE;
+
+		if (driver->ref_count == 0)
+			diagmem_init(driver);
+		driver->ref_count++;
+		mutex_unlock(&driver->diagchar_mutex);
+		return 0;
+	}
+	return -ENOMEM;
+
+fail:
+	mutex_unlock(&driver->diagchar_mutex);
+	driver->num_clients--;
+	pr_alert("diag: Insufficient memory for new client");
+	return -ENOMEM;
+}
+
+static int diagchar_close(struct inode *inode, struct file *file)
+{
+	int i = 0;
+	struct diagchar_priv *diagpriv_data = file->private_data;
+
+	if (!(file->private_data)) {
+		pr_alert("diag: Invalid file pointer");
+		return -ENOMEM;
+	}
+
+#ifdef CONFIG_DIAG_OVER_USB
+	/* If the SD logging process exits, change logging to USB mode */
+	if (driver->logging_process_id == current->tgid) {
+		driver->logging_mode = USB_MODE;
+		diagfwd_connect();
+	}
+#endif /* DIAG over USB */
+	/* Delete the pkt response table entry for the exiting process */
+	for (i = 0; i < diag_max_registration; i++)
+			if (driver->table[i].process_id == current->tgid)
+					driver->table[i].process_id = 0;
+
+	if (driver) {
+		mutex_lock(&driver->diagchar_mutex);
+		driver->ref_count--;
+		/* On Client exit, try to destroy all 3 pools */
+		diagmem_exit(driver, POOL_TYPE_COPY);
+		diagmem_exit(driver, POOL_TYPE_HDLC);
+		diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
+		for (i = 0; i < driver->num_clients; i++) {
+			if (NULL != diagpriv_data && diagpriv_data->pid ==
+				 driver->client_map[i].pid) {
+				driver->client_map[i].pid = 0;
+				kfree(diagpriv_data);
+				diagpriv_data = NULL;
+				break;
+			}
+		}
+		mutex_unlock(&driver->diagchar_mutex);
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+void diag_fill_reg_table(int j, struct bindpkt_params *params,
+					  int *success, int *count_entries)
+{
+	*success = 1;
+	driver->table[j].cmd_code = params->cmd_code;
+	driver->table[j].subsys_id = params->subsys_id;
+	driver->table[j].cmd_code_lo = params->cmd_code_lo;
+	driver->table[j].cmd_code_hi = params->cmd_code_hi;
+	if (params->proc_id == APPS_PROC) {
+		driver->table[j].process_id = current->tgid;
+		driver->table[j].client_id = APPS_PROC;
+	} else {
+		driver->table[j].process_id = NON_APPS_PROC;
+		driver->table[j].client_id = params->client_id;
+	}
+	(*count_entries)++;
+}
+
+long diagchar_ioctl(struct file *filp,
+			   unsigned int iocmd, unsigned long ioarg)
+{
+	int i, j, count_entries = 0, temp;
+	int success = -1;
+	void *temp_buf;
+
+	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
+		struct bindpkt_params_per_process *pkt_params =
+			 (struct bindpkt_params_per_process *) ioarg;
+		mutex_lock(&driver->diagchar_mutex);
+		for (i = 0; i < diag_max_registration; i++) {
+			if (driver->table[i].process_id == 0) {
+				diag_fill_reg_table(i, pkt_params->params,
+						&success, &count_entries);
+				if (pkt_params->count > count_entries) {
+					pkt_params->params++;
+				} else {
+					mutex_unlock(&driver->diagchar_mutex);
+					return success;
+				}
+			}
+		}
+		if (i < diag_threshold_registration) {
+			/* Increase table size by amount required */
+			diag_max_registration += pkt_params->count -
+							 count_entries;
+			/* Make sure size doesnt go beyond threshold */
+			if (diag_max_registration > diag_threshold_registration)
+				diag_max_registration =
+						 diag_threshold_registration;
+			temp_buf = krealloc(driver->table,
+					 diag_max_registration*sizeof(struct
+					 diag_master_table), GFP_KERNEL);
+			if (!temp_buf) {
+				diag_max_registration -= pkt_params->count -
+							 count_entries;
+				pr_alert("diag: Insufficient memory for reg.");
+				mutex_unlock(&driver->diagchar_mutex);
+				return 0;
+			} else {
+				driver->table = temp_buf;
+			}
+			for (j = i; j < diag_max_registration; j++) {
+				diag_fill_reg_table(j, pkt_params->params,
+						&success, &count_entries);
+				if (pkt_params->count > count_entries) {
+					pkt_params->params++;
+				} else {
+					mutex_unlock(&driver->diagchar_mutex);
+					return success;
+				}
+			}
+		} else {
+			mutex_unlock(&driver->diagchar_mutex);
+			pr_err("Max size reached, Pkt Registration failed for"
+						" Process %d", current->tgid);
+		}
+		success = 0;
+	} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
+		struct diagpkt_delay_params *delay_params =
+					(struct diagpkt_delay_params *) ioarg;
+
+		if ((delay_params->rsp_ptr) &&
+		 (delay_params->size == sizeof(delayed_rsp_id)) &&
+				 (delay_params->num_bytes_ptr)) {
+			*((uint16_t *)delay_params->rsp_ptr) =
+				DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
+			*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+			success = 0;
+		}
+	} else if (iocmd == DIAG_IOCTL_LSM_DEINIT) {
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid == current->tgid)
+				break;
+		if (i == -1)
+			return -EINVAL;
+		driver->data_ready[i] |= DEINIT_TYPE;
+		wake_up_interruptible(&driver->wait_q);
+		success = 1;
+	} else if (iocmd == DIAG_IOCTL_SWITCH_LOGGING) {
+		mutex_lock(&driver->diagchar_mutex);
+		temp = driver->logging_mode;
+		driver->logging_mode = (int)ioarg;
+		driver->logging_process_id = current->tgid;
+		mutex_unlock(&driver->diagchar_mutex);
+		if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
+							== NO_LOGGING_MODE) {
+			driver->in_busy_1 = 1;
+			driver->in_busy_2 = 1;
+			driver->in_busy_qdsp_1 = 1;
+			driver->in_busy_qdsp_2 = 1;
+		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
+							== MEMORY_DEVICE_MODE) {
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			driver->in_busy_qdsp_1 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			/* Poll SMD channels to check for data*/
+			if (driver->ch)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_work));
+			if (driver->chqdsp)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_qdsp_work));
+		}
+#ifdef CONFIG_DIAG_OVER_USB
+		else if (temp == USB_MODE && driver->logging_mode
+							 == NO_LOGGING_MODE)
+			diagfwd_disconnect();
+		else if (temp == NO_LOGGING_MODE && driver->logging_mode
+								== USB_MODE)
+			diagfwd_connect();
+		else if (temp == USB_MODE && driver->logging_mode
+							== MEMORY_DEVICE_MODE) {
+			diagfwd_disconnect();
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			/* Poll SMD channels to check for data*/
+			if (driver->ch)
+				queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_work));
+			if (driver->chqdsp)
+				queue_work(driver->diag_wq,
+					&(driver->diag_read_smd_qdsp_work));
+		} else if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
+								== USB_MODE)
+			diagfwd_connect();
+#endif /* DIAG over USB */
+		success = 1;
+	}
+
+	return success;
+}
+
+static int diagchar_read(struct file *file, char __user *buf, size_t count,
+			  loff_t *ppos)
+{
+	int index = -1, i = 0, ret = 0;
+	int num_data = 0, data_type;
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid == current->tgid)
+			index = i;
+
+	if (index == -1) {
+		pr_err("diag: Client PID not found in table");
+		return -EINVAL;
+	}
+
+	wait_event_interruptible(driver->wait_q,
+				  driver->data_ready[index]);
+	mutex_lock(&driver->diagchar_mutex);
+
+	if ((driver->data_ready[index] & MEMORY_DEVICE_LOG_TYPE) && (driver->
+					logging_mode == MEMORY_DEVICE_MODE)) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & MEMORY_DEVICE_LOG_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		/* place holder for number of data field */
+		ret += 4;
+
+		for (i = 0; i < driver->poolsize_write_struct; i++) {
+			if (driver->buf_tbl[i].length > 0) {
+#ifdef DIAG_DEBUG
+				pr_debug("diag: WRITING the buf address "
+				       "and length is %x , %d\n", (unsigned int)
+					(driver->buf_tbl[i].buf),
+					driver->buf_tbl[i].length);
+#endif
+				num_data++;
+				/* Copy the length of data being passed */
+				if (copy_to_user(buf+ret, (void *)&(driver->
+						buf_tbl[i].length), 4)) {
+						num_data--;
+						goto drop;
+				}
+				ret += 4;
+
+				/* Copy the actual data being passed */
+				if (copy_to_user(buf+ret, (void *)driver->
+				buf_tbl[i].buf, driver->buf_tbl[i].length)) {
+					ret -= 4;
+					num_data--;
+					goto drop;
+				}
+				ret += driver->buf_tbl[i].length;
+drop:
+#ifdef DIAG_DEBUG
+				pr_debug("diag: DEQUEUE buf address and"
+				       " length is %x,%d\n", (unsigned int)
+				       (driver->buf_tbl[i].buf), driver->
+				       buf_tbl[i].length);
+#endif
+				diagmem_free(driver, (unsigned char *)
+				(driver->buf_tbl[i].buf), POOL_TYPE_HDLC);
+				driver->buf_tbl[i].length = 0;
+				driver->buf_tbl[i].buf = 0;
+			}
+		}
+
+		/* copy modem data */
+		if (driver->in_busy_1 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 (driver->write_ptr_1->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(driver->buf_in_1),
+					 driver->write_ptr_1->length);
+			driver->in_busy_1 = 0;
+		}
+		if (driver->in_busy_2 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 (driver->write_ptr_2->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					 *(driver->buf_in_2),
+					 driver->write_ptr_2->length);
+			driver->in_busy_2 = 0;
+		}
+
+		/* copy q6 data */
+		if (driver->in_busy_qdsp_1 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_qdsp_1->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+							buf_in_qdsp_1),
+					 driver->write_ptr_qdsp_1->length);
+			driver->in_busy_qdsp_1 = 0;
+		}
+		if (driver->in_busy_qdsp_2 == 1) {
+			num_data++;
+			/*Copy the length of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+				 (driver->write_ptr_qdsp_2->length), 4);
+			/*Copy the actual data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
+				buf_in_qdsp_2), driver->
+					write_ptr_qdsp_2->length);
+			driver->in_busy_qdsp_2 = 0;
+		}
+
+		/* copy number of data fields */
+		COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
+		ret -= 4;
+		driver->data_ready[index] ^= MEMORY_DEVICE_LOG_TYPE;
+		if (driver->ch)
+			queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_work));
+		if (driver->chqdsp)
+			queue_work(driver->diag_wq,
+					 &(driver->diag_read_smd_qdsp_work));
+		APPEND_DEBUG('n');
+		goto exit;
+	} else if (driver->data_ready[index] & MEMORY_DEVICE_LOG_TYPE) {
+		/* In case, the thread wakes up and the logging mode is
+		not memory device any more, the condition needs to be cleared */
+		driver->data_ready[index] ^= MEMORY_DEVICE_LOG_TYPE;
+	}
+
+	if (driver->data_ready[index] & DEINIT_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DEINIT_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		driver->data_ready[index] ^= DEINIT_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & MSG_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & MSG_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->msg_masks),
+							 MSG_MASK_SIZE);
+		driver->data_ready[index] ^= MSG_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & EVENT_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & EVENT_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->event_masks),
+							 EVENT_MASK_SIZE);
+		driver->data_ready[index] ^= EVENT_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & LOG_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & LOG_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->log_masks),
+							 LOG_MASK_SIZE);
+		driver->data_ready[index] ^= LOG_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & PKT_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & PKT_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->pkt_buf),
+							 driver->pkt_length);
+		driver->data_ready[index] ^= PKT_TYPE;
+		goto exit;
+	}
+
+exit:
+	mutex_unlock(&driver->diagchar_mutex);
+	return ret;
+}
+
+static int diagchar_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	int err, ret = 0, pkt_type;
+#ifdef DIAG_DEBUG
+	int length = 0, i;
+#endif
+	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
+	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
+	void *buf_copy = NULL;
+	int payload_size;
+#ifdef CONFIG_DIAG_OVER_USB
+	if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
+				(driver->logging_mode == NO_LOGGING_MODE)) {
+		/*Drop the diag payload */
+		return -EIO;
+	}
+#endif /* DIAG over USB */
+	/* Get the packet type F3/log/event/Pkt response */
+	err = copy_from_user((&pkt_type), buf, 4);
+	/*First 4 bytes indicate the type of payload - ignore these */
+	payload_size = count - 4;
+
+	if (pkt_type == MEMORY_DEVICE_LOG_TYPE) {
+		if (!mask_request_validate((unsigned char *)buf)) {
+			printk(KERN_ALERT "mask request Invalid ..cannot send to modem \n");
+			return -EFAULT;
+		}
+		buf = buf + 4;
+#ifdef DIAG_DEBUG
+		pr_debug("diag: masks: %d\n", payload_size);
+		for (i = 0; i < payload_size; i++)
+			printk(KERN_DEBUG "\t %x", *(((unsigned char *)buf)+i));
+#endif
+		diag_process_hdlc((void *)buf, payload_size);
+		return 0;
+	}
+
+	buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY);
+	if (!buf_copy) {
+		driver->dropped_count++;
+		return -ENOMEM;
+	}
+
+	err = copy_from_user(buf_copy, buf + 4, payload_size);
+	if (err) {
+		printk(KERN_INFO "diagchar : copy_from_user failed\n");
+		ret = -EFAULT;
+		goto fail_free_copy;
+	}
+#ifdef DIAG_DEBUG
+	printk(KERN_DEBUG "data is -->\n");
+	for (i = 0; i < payload_size; i++)
+		printk(KERN_DEBUG "\t %x \t", *(((unsigned char *)buf_copy)+i));
+#endif
+	send.state = DIAG_STATE_START;
+	send.pkt = buf_copy;
+	send.last = (void *)(buf_copy + payload_size - 1);
+	send.terminate = 1;
+#ifdef DIAG_DEBUG
+	pr_debug("diag: Already used bytes in buffer %d, and"
+	" incoming payload size is %d\n", driver->used, payload_size);
+	printk(KERN_DEBUG "hdlc encoded data is -->\n");
+	for (i = 0; i < payload_size + 8; i++) {
+		printk(KERN_DEBUG "\t %x \t", *(((unsigned char *)buf_hdlc)+i));
+		if (*(((unsigned char *)buf_hdlc)+i) != 0x7e)
+			length++;
+	}
+#endif
+	mutex_lock(&driver->diagchar_mutex);
+	if (!buf_hdlc)
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+						 POOL_TYPE_HDLC);
+	if (!buf_hdlc) {
+		ret = -ENOMEM;
+		goto fail_free_hdlc;
+	}
+	if (HDLC_OUT_BUF_SIZE - driver->used <= (2*payload_size) + 3) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+							 POOL_TYPE_HDLC);
+		if (!buf_hdlc) {
+			ret = -ENOMEM;
+			goto fail_free_hdlc;
+		}
+	}
+
+	enc.dest = buf_hdlc + driver->used;
+	enc.dest_last = (void *)(buf_hdlc + driver->used + 2*payload_size + 3);
+	diag_hdlc_encode(&send, &enc);
+
+	/* This is to check if after HDLC encoding, we are still within the
+	 limits of aggregation buffer. If not, we write out the current buffer
+	and start aggregation in a newly allocated buffer */
+	if ((unsigned int) enc.dest >=
+		 (unsigned int)(buf_hdlc + HDLC_OUT_BUF_SIZE)) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
+							 POOL_TYPE_HDLC);
+		if (!buf_hdlc) {
+			ret = -ENOMEM;
+			goto fail_free_hdlc;
+		}
+		enc.dest = buf_hdlc + driver->used;
+		enc.dest_last = (void *)(buf_hdlc + driver->used +
+							 (2*payload_size) + 3);
+		diag_hdlc_encode(&send, &enc);
+	}
+
+	driver->used = (uint32_t) enc.dest - (uint32_t) buf_hdlc;
+	if (pkt_type == DATA_TYPE_RESPONSE) {
+		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
+		if (err) {
+			/*Free the buffer right away if write failed */
+			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
+			diagmem_free(driver, (unsigned char *)driver->
+				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			ret = -EIO;
+			goto fail_free_hdlc;
+		}
+		buf_hdlc = NULL;
+		driver->used = 0;
+	}
+
+	mutex_unlock(&driver->diagchar_mutex);
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	if (!timer_in_progress)	{
+		timer_in_progress = 1;
+		ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
+	}
+	return 0;
+
+fail_free_hdlc:
+	buf_hdlc = NULL;
+	driver->used = 0;
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	mutex_unlock(&driver->diagchar_mutex);
+	return ret;
+
+fail_free_copy:
+	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	return ret;
+}
+
+int mask_request_validate(unsigned char mask_buf[])
+{
+	uint8_t packet_id;
+	uint8_t subsys_id;
+	uint16_t ss_cmd;
+
+	packet_id = mask_buf[4];
+
+	if (packet_id == 0x4B) {
+		subsys_id = mask_buf[5];
+		ss_cmd = *(uint16_t *)(mask_buf + 6);
+		/* Packets with SSID which are allowed */
+		switch (subsys_id) {
+		case 0x04: /* DIAG_SUBSYS_WCDMA */
+			if ((ss_cmd == 0) || (ss_cmd == 0xF))
+				return 1;
+			break;
+		case 0x08: /* DIAG_SUBSYS_GSM */
+			if ((ss_cmd == 0) || (ss_cmd == 0x1))
+				return 1;
+			break;
+		case 0x09: /* DIAG_SUBSYS_UMTS */
+		case 0x0F: /* DIAG_SUBSYS_CM */
+			if (ss_cmd == 0)
+				return 1;
+			break;
+		case 0x0C: /* DIAG_SUBSYS_OS */
+			if ((ss_cmd == 2) || (ss_cmd == 0x100))
+				return 1; /* MPU and APU */
+			break;
+		case 0x12: /* DIAG_SUBSYS_DIAG_SERV */
+			if ((ss_cmd == 0) || (ss_cmd == 0x6) || (ss_cmd == 0x7))
+				return 1;
+			break;
+		case 0x13: /* DIAG_SUBSYS_FS */
+			if ((ss_cmd == 0) || (ss_cmd == 0x1))
+				return 1;
+			break;
+		default:
+			return 0;
+			break;
+		}
+	} else {
+		switch (packet_id) {
+		case 0x00:    /* Version Number */
+		case 0x0C:    /* CDMA status packet */
+		case 0x1C:    /* Diag Version */
+		case 0x1D:    /* Time Stamp */
+		case 0x60:    /* Event Report Control */
+		case 0x63:    /* Status snapshot */
+		case 0x73:    /* Logging Configuration */
+		case 0x7C:    /* Extended build ID */
+		case 0x7D:    /* Extended Message configuration */
+		case 0x81:    /* Event get mask */
+		case 0x82:    /* Set the event mask */
+			return 1;
+			break;
+		default:
+			return 0;
+			break;
+		}
+	}
+	return 0;
+}
+
+static const struct file_operations diagcharfops = {
+	.owner = THIS_MODULE,
+	.read = diagchar_read,
+	.write = diagchar_write,
+	.unlocked_ioctl = diagchar_ioctl,
+	.open = diagchar_open,
+	.release = diagchar_close
+};
+
+static int diagchar_setup_cdev(dev_t devno)
+{
+
+	int err;
+
+	cdev_init(driver->cdev, &diagcharfops);
+
+	driver->cdev->owner = THIS_MODULE;
+	driver->cdev->ops = &diagcharfops;
+
+	err = cdev_add(driver->cdev, devno, 1);
+
+	if (err) {
+		printk(KERN_INFO "diagchar cdev registration failed !\n\n");
+		return -1;
+	}
+
+	driver->diagchar_class = class_create(THIS_MODULE, "diag");
+
+	if (IS_ERR(driver->diagchar_class)) {
+		printk(KERN_ERR "Error creating diagchar class.\n");
+		return -1;
+	}
+
+	device_create(driver->diagchar_class, NULL, devno,
+				  (void *)driver, "diag");
+
+	return 0;
+
+}
+
+static int diagchar_cleanup(void)
+{
+	if (driver) {
+		if (driver->cdev) {
+			/* TODO - Check if device exists before deleting */
+			device_destroy(driver->diagchar_class,
+				       MKDEV(driver->major,
+					     driver->minor_start));
+			cdev_del(driver->cdev);
+		}
+		if (!IS_ERR(driver->diagchar_class))
+			class_destroy(driver->diagchar_class);
+		kfree(driver);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_DIAG_SDIO_PIPE
+void diag_sdio_fn(int type)
+{
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) {
+		if (type == INIT)
+			diagfwd_sdio_init();
+		else if (type == EXIT)
+			diagfwd_sdio_exit();
+	}
+}
+#else
+inline void diag_sdio_fn(int type) {}
+#endif
+
+static int __init diagchar_init(void)
+{
+	dev_t dev;
+	int error;
+
+	pr_debug("diagfwd initializing ..\n");
+	driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL);
+
+	if (driver) {
+		driver->used = 0;
+		timer_in_progress = 0;
+		driver->debug_flag = 1;
+		setup_timer(&drain_timer, drain_timer_func, 1234);
+		driver->itemsize = itemsize;
+		driver->poolsize = poolsize;
+		driver->itemsize_hdlc = itemsize_hdlc;
+		driver->poolsize_hdlc = poolsize_hdlc;
+		driver->itemsize_write_struct = itemsize_write_struct;
+		driver->poolsize_write_struct = poolsize_write_struct;
+		driver->num_clients = max_clients;
+		driver->logging_mode = USB_MODE;
+		mutex_init(&driver->diagchar_mutex);
+		init_waitqueue_head(&driver->wait_q);
+		INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_cntl_work),
+						 diag_read_smd_cntl_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_qdsp_work),
+			   diag_read_smd_qdsp_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_qdsp_cntl_work),
+			   diag_read_smd_qdsp_cntl_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_wcnss_work),
+			diag_read_smd_wcnss_work_fn);
+		INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
+			diag_read_smd_wcnss_cntl_work_fn);
+		diagfwd_init();
+		diagfwd_cntl_init();
+		diag_sdio_fn(INIT);
+		pr_debug("diagchar initializing ..\n");
+		driver->num = 1;
+		driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
+		strlcpy(driver->name, "diag", 4);
+
+		/* Get major number from kernel and initialize */
+		error = alloc_chrdev_region(&dev, driver->minor_start,
+					    driver->num, driver->name);
+		if (!error) {
+			driver->major = MAJOR(dev);
+			driver->minor_start = MINOR(dev);
+		} else {
+			printk(KERN_INFO "Major number not allocated\n");
+			goto fail;
+		}
+		driver->cdev = cdev_alloc();
+		error = diagchar_setup_cdev(dev);
+		if (error)
+			goto fail;
+	} else {
+		printk(KERN_INFO "kzalloc failed\n");
+		goto fail;
+	}
+
+	pr_info("diagchar initialized now");
+	return 0;
+
+fail:
+	diagchar_cleanup();
+	diagfwd_exit();
+	diagfwd_cntl_exit();
+	diag_sdio_fn(EXIT);
+	return -1;
+}
+
+static void __exit diagchar_exit(void)
+{
+	printk(KERN_INFO "diagchar exiting ..\n");
+	/* On Driver exit, send special pool type to
+	 ensure no memory leaks */
+	diagmem_exit(driver, POOL_TYPE_ALL);
+	diagfwd_exit();
+	diagfwd_cntl_exit();
+	diag_sdio_fn(EXIT);
+	diagchar_cleanup();
+	printk(KERN_INFO "done diagchar exit\n");
+}
+
+module_init(diagchar_init);
+module_exit(diagchar_exit);
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
new file mode 100644
index 0000000..ef57d52
--- /dev/null
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2008-2009, 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/init.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/crc-ccitt.h>
+#include "diagchar_hdlc.h"
+
+
+MODULE_LICENSE("GPL v2");
+
+#define CRC_16_L_SEED           0xFFFF
+
+#define CRC_16_L_STEP(xx_crc, xx_c) \
+	crc_ccitt_byte(xx_crc, xx_c)
+
+void diag_hdlc_encode(struct diag_send_desc_type *src_desc,
+		      struct diag_hdlc_dest_type *enc)
+{
+	uint8_t *dest;
+	uint8_t *dest_last;
+	const uint8_t *src;
+	const uint8_t *src_last;
+	uint16_t crc;
+	unsigned char src_byte = 0;
+	enum diag_send_state_enum_type state;
+	unsigned int used = 0;
+
+	if (src_desc && enc) {
+
+		/* Copy parts to local variables. */
+		src = src_desc->pkt;
+		src_last = src_desc->last;
+		state = src_desc->state;
+		dest = enc->dest;
+		dest_last = enc->dest_last;
+
+		if (state == DIAG_STATE_START) {
+			crc = CRC_16_L_SEED;
+			state++;
+		} else {
+			/* Get a local copy of the CRC */
+			crc = enc->crc;
+		}
+
+		/* dest or dest_last may be NULL to trigger a
+		   state transition only */
+		if (dest && dest_last) {
+			/* This condition needs to include the possibility
+			   of 2 dest bytes for an escaped byte */
+			while (src <= src_last && dest <= dest_last) {
+
+				src_byte = *src++;
+
+				if ((src_byte == CONTROL_CHAR) ||
+				    (src_byte == ESC_CHAR)) {
+
+					/* If the escape character is not the
+					   last byte */
+					if (dest != dest_last) {
+						crc = CRC_16_L_STEP(crc,
+								    src_byte);
+
+						*dest++ = ESC_CHAR;
+						used++;
+
+						*dest++ = src_byte
+							  ^ ESC_MASK;
+						used++;
+					} else {
+
+						src--;
+						break;
+					}
+
+				} else {
+					crc = CRC_16_L_STEP(crc, src_byte);
+					*dest++ = src_byte;
+					used++;
+				}
+			}
+
+			if (src > src_last) {
+
+				if (state == DIAG_STATE_BUSY) {
+					if (src_desc->terminate) {
+						crc = ~crc;
+						state++;
+					} else {
+						/* Done with fragment */
+						state = DIAG_STATE_COMPLETE;
+					}
+				}
+
+				while (dest <= dest_last &&
+				       state >= DIAG_STATE_CRC1 &&
+				       state < DIAG_STATE_TERM) {
+					/* Encode a byte of the CRC next */
+					src_byte = crc & 0xFF;
+
+					if ((src_byte == CONTROL_CHAR)
+					    || (src_byte == ESC_CHAR)) {
+
+						if (dest != dest_last) {
+
+							*dest++ = ESC_CHAR;
+							used++;
+							*dest++ = src_byte ^
+								  ESC_MASK;
+							used++;
+
+							crc >>= 8;
+						} else {
+
+							break;
+						}
+					} else {
+
+						crc >>= 8;
+						*dest++ = src_byte;
+						used++;
+					}
+
+					state++;
+				}
+
+				if (state == DIAG_STATE_TERM) {
+					if (dest_last >= dest) {
+						*dest++ = CONTROL_CHAR;
+						used++;
+						state++;	/* Complete */
+					}
+				}
+			}
+		}
+		/* Copy local variables back into the encode structure. */
+
+		enc->dest = dest;
+		enc->dest_last = dest_last;
+		enc->crc = crc;
+		src_desc->pkt = src;
+		src_desc->last = src_last;
+		src_desc->state = state;
+	}
+
+	return;
+}
+
+
+int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc)
+{
+	uint8_t *src_ptr = NULL, *dest_ptr = NULL;
+	unsigned int src_length = 0, dest_length = 0;
+
+	unsigned int len = 0;
+	unsigned int i;
+	uint8_t src_byte;
+
+	int pkt_bnd = 0;
+
+	if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
+	    (hdlc->src_size - hdlc->src_idx > 0) &&
+	    (hdlc->dest_size - hdlc->dest_idx > 0)) {
+
+		src_ptr = hdlc->src_ptr;
+		src_ptr = &src_ptr[hdlc->src_idx];
+		src_length = hdlc->src_size - hdlc->src_idx;
+
+		dest_ptr = hdlc->dest_ptr;
+		dest_ptr = &dest_ptr[hdlc->dest_idx];
+		dest_length = hdlc->dest_size - hdlc->dest_idx;
+
+		for (i = 0; i < src_length; i++) {
+
+			src_byte = src_ptr[i];
+
+			if (hdlc->escaping) {
+				dest_ptr[len++] = src_byte ^ ESC_MASK;
+				hdlc->escaping = 0;
+			} else if (src_byte == ESC_CHAR) {
+				if (i == (src_length - 1)) {
+					hdlc->escaping = 1;
+					i++;
+					break;
+				} else {
+					dest_ptr[len++] = src_ptr[++i]
+							  ^ ESC_MASK;
+				}
+			} else if (src_byte == CONTROL_CHAR) {
+				dest_ptr[len++] = src_byte;
+				pkt_bnd = 1;
+				i++;
+				break;
+			} else {
+				dest_ptr[len++] = src_byte;
+			}
+
+			if (len >= dest_length) {
+				i++;
+				break;
+			}
+		}
+
+		hdlc->src_idx += i;
+		hdlc->dest_idx += len;
+	}
+
+	return pkt_bnd;
+}
diff --git a/drivers/char/diag/diagchar_hdlc.h b/drivers/char/diag/diagchar_hdlc.h
new file mode 100644
index 0000000..2df81de
--- /dev/null
+++ b/drivers/char/diag/diagchar_hdlc.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2008-2009, 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.
+ */
+
+#ifndef DIAGCHAR_HDLC
+#define DIAGCHAR_HDLC
+
+enum diag_send_state_enum_type {
+	DIAG_STATE_START,
+	DIAG_STATE_BUSY,
+	DIAG_STATE_CRC1,
+	DIAG_STATE_CRC2,
+	DIAG_STATE_TERM,
+	DIAG_STATE_COMPLETE
+};
+
+struct diag_send_desc_type {
+	const void *pkt;
+	const void *last;	/* Address of last byte to send. */
+	enum diag_send_state_enum_type state;
+	unsigned char terminate;	/* True if this fragment
+					   terminates the packet */
+};
+
+struct diag_hdlc_dest_type {
+	void *dest;
+	void *dest_last;
+	/* Below: internal use only */
+	uint16_t crc;
+};
+
+struct diag_hdlc_decode_type {
+	uint8_t *src_ptr;
+	unsigned int src_idx;
+	unsigned int src_size;
+	uint8_t *dest_ptr;
+	unsigned int dest_idx;
+	unsigned int dest_size;
+	int escaping;
+
+};
+
+void diag_hdlc_encode(struct diag_send_desc_type *src_desc,
+		      struct diag_hdlc_dest_type *enc);
+
+int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc);
+
+#define ESC_CHAR     0x7D
+#define CONTROL_CHAR 0x7E
+#define ESC_MASK     0x20
+
+#endif
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
new file mode 100644
index 0000000..433f09a
--- /dev/null
+++ b/drivers/char/diag/diagfwd.c
@@ -0,0 +1,1384 @@
+/* Copyright (c) 2008-2011, 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/diagchar.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include <mach/msm_smd.h>
+#include <mach/socinfo.h>
+#include <mach/restart.h>
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+#include "diagchar_hdlc.h"
+#ifdef CONFIG_DIAG_SDIO_PIPE
+#include "diagfwd_sdio.h"
+#endif
+#define MODE_CMD	41
+#define RESET_ID	2
+
+int diag_debug_buf_idx;
+unsigned char diag_debug_buf[1024];
+static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */
+struct diag_master_table entry;
+
+struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
+struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
+
+#define ENCODE_RSP_AND_SEND(buf_length)				\
+do {									\
+	send.state = DIAG_STATE_START;					\
+	send.pkt = driver->apps_rsp_buf;				\
+	send.last = (void *)(driver->apps_rsp_buf + buf_length);	\
+	send.terminate = 1;						\
+	if (!driver->in_busy_1) {					\
+		enc.dest = driver->buf_in_1;				\
+		enc.dest_last = (void *)(driver->buf_in_1 + 499);	\
+		diag_hdlc_encode(&send, &enc);				\
+		driver->write_ptr_1->buf = driver->buf_in_1;		\
+		driver->write_ptr_1->length = (int)(enc.dest - \
+						(void *)(driver->buf_in_1)); \
+		usb_diag_write(driver->legacy_ch, driver->write_ptr_1);	\
+		memset(driver->apps_rsp_buf, '\0', 500);		\
+	}								\
+} while (0)
+
+#define CHK_OVERFLOW(bufStart, start, end, length) \
+((bufStart <= start) && (end - start >= length)) ? 1 : 0
+
+int chk_config_get_id()
+{
+	switch (socinfo_get_id()) {
+	case APQ8060_MACHINE_ID:
+	case MSM8660_MACHINE_ID:
+		return APQ8060_TOOLS_ID;
+	case AO8960_MACHINE_ID:
+		return AO8960_TOOLS_ID;
+	default:
+		return 0;
+	}
+}
+
+void __diag_smd_send_req(void)
+{
+	void *buf = NULL;
+	int *in_busy_ptr = NULL;
+	struct diag_request *write_ptr_modem = NULL;
+
+	if (!driver->in_busy_1) {
+		buf = driver->buf_in_1;
+		write_ptr_modem = driver->write_ptr_1;
+		in_busy_ptr = &(driver->in_busy_1);
+	} else if (!driver->in_busy_2) {
+		buf = driver->buf_in_2;
+		write_ptr_modem = driver->write_ptr_2;
+		in_busy_ptr = &(driver->in_busy_2);
+	}
+
+	if (driver->ch && buf) {
+		int r = smd_read_avail(driver->ch);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SMD sending in "
+						   "packets upto %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SMD sending in "
+				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				pr_info("Out of diagmem for Modem\n");
+			else {
+				APPEND_DEBUG('i');
+				smd_read(driver->ch, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_modem->length = r;
+				*in_busy_ptr = 1;
+				diag_device_write(buf, MODEM_DATA,
+							 write_ptr_modem);
+			}
+		}
+	}
+}
+
+int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr)
+{
+	int i, err = 0;
+
+	if (driver->logging_mode == MEMORY_DEVICE_MODE) {
+		if (proc_num == APPS_DATA) {
+			for (i = 0; i < driver->poolsize_write_struct; i++)
+				if (driver->buf_tbl[i].length == 0) {
+					driver->buf_tbl[i].buf = buf;
+					driver->buf_tbl[i].length =
+								 driver->used;
+#ifdef DIAG_DEBUG
+					pr_debug("diag: ENQUEUE buf ptr"
+						   " and length is %x , %d\n",
+						   (unsigned int)(driver->buf_
+				tbl[i].buf), driver->buf_tbl[i].length);
+#endif
+					break;
+				}
+		}
+		for (i = 0; i < driver->num_clients; i++)
+			if (driver->client_map[i].pid ==
+						 driver->logging_process_id)
+				break;
+		if (i < driver->num_clients) {
+			driver->data_ready[i] |= MEMORY_DEVICE_LOG_TYPE;
+			wake_up_interruptible(&driver->wait_q);
+		} else
+			return -EINVAL;
+	} else if (driver->logging_mode == NO_LOGGING_MODE) {
+		if (proc_num == MODEM_DATA) {
+			driver->in_busy_1 = 0;
+			driver->in_busy_2 = 0;
+			queue_work(driver->diag_wq, &(driver->
+							diag_read_smd_work));
+		} else if (proc_num == QDSP_DATA) {
+			driver->in_busy_qdsp_1 = 0;
+			driver->in_busy_qdsp_2 = 0;
+			queue_work(driver->diag_wq, &(driver->
+						diag_read_smd_qdsp_work));
+		}  else if (proc_num == WCNSS_DATA) {
+			driver->in_busy_wcnss = 0;
+			queue_work(driver->diag_wq, &(driver->
+				diag_read_smd_wcnss_work));
+		}
+		err = -1;
+	}
+#ifdef CONFIG_DIAG_OVER_USB
+	else if (driver->logging_mode == USB_MODE) {
+		if (proc_num == APPS_DATA) {
+			driver->write_ptr_svc = (struct diag_request *)
+			(diagmem_alloc(driver, sizeof(struct diag_request),
+				 POOL_TYPE_WRITE_STRUCT));
+			if (driver->write_ptr_svc) {
+				driver->write_ptr_svc->length = driver->used;
+				driver->write_ptr_svc->buf = buf;
+				err = usb_diag_write(driver->legacy_ch,
+						driver->write_ptr_svc);
+			} else
+				err = -1;
+		} else if (proc_num == MODEM_DATA) {
+			write_ptr->buf = buf;
+#ifdef DIAG_DEBUG
+			printk(KERN_INFO "writing data to USB,"
+				"pkt length %d\n", write_ptr->length);
+			print_hex_dump(KERN_DEBUG, "Written Packet Data to"
+					   " USB: ", 16, 1, DUMP_PREFIX_ADDRESS,
+					    buf, write_ptr->length, 1);
+#endif /* DIAG DEBUG */
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		} else if (proc_num == QDSP_DATA) {
+			write_ptr->buf = buf;
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		} else if (proc_num == WCNSS_DATA) {
+			write_ptr->buf = buf;
+			err = usb_diag_write(driver->legacy_ch, write_ptr);
+		}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+		else if (proc_num == SDIO_DATA) {
+			if (machine_is_msm8x60_fusion() ||
+					machine_is_msm8x60_fusn_ffa()) {
+				write_ptr->buf = buf;
+				err = usb_diag_write(driver->mdm_ch, write_ptr);
+			} else
+				pr_err("diag: Incorrect data while USB write");
+		}
+#endif
+		APPEND_DEBUG('d');
+	}
+#endif /* DIAG OVER USB */
+    return err;
+}
+
+void __diag_smd_wcnss_send_req(void)
+{
+	void *buf = driver->buf_in_wcnss;
+	int *in_busy_wcnss_ptr = &(driver->in_busy_wcnss);
+	struct diag_request *write_ptr_wcnss = driver->write_ptr_wcnss;
+
+	if (driver->ch_wcnss && buf) {
+		int r = smd_read_avail(driver->ch_wcnss);
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: wcnss packets > %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: wcnss pkt > %d", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf) {
+				pr_err("Out of diagmem for wcnss\n");
+			} else {
+				APPEND_DEBUG('i');
+				smd_read(driver->ch_wcnss, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_wcnss->length = r;
+				*in_busy_wcnss_ptr = 1;
+				diag_device_write(buf, WCNSS_DATA,
+					 write_ptr_wcnss);
+			}
+		}
+	}
+}
+
+void __diag_smd_qdsp_send_req(void)
+{
+	void *buf = NULL;
+	int *in_busy_qdsp_ptr = NULL;
+	struct diag_request *write_ptr_qdsp = NULL;
+
+	if (!driver->in_busy_qdsp_1) {
+		buf = driver->buf_in_qdsp_1;
+		write_ptr_qdsp = driver->write_ptr_qdsp_1;
+		in_busy_qdsp_ptr = &(driver->in_busy_qdsp_1);
+	} else if (!driver->in_busy_qdsp_2) {
+		buf = driver->buf_in_qdsp_2;
+		write_ptr_qdsp = driver->write_ptr_qdsp_2;
+		in_busy_qdsp_ptr = &(driver->in_busy_qdsp_2);
+	}
+
+	if (driver->chqdsp && buf) {
+		int r = smd_read_avail(driver->chqdsp);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SMD sending in "
+						   "packets upto %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SMD sending in "
+				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				printk(KERN_INFO "Out of diagmem for QDSP\n");
+			else {
+				APPEND_DEBUG('i');
+				smd_read(driver->chqdsp, buf, r);
+				APPEND_DEBUG('j');
+				write_ptr_qdsp->length = r;
+				*in_busy_qdsp_ptr = 1;
+				diag_device_write(buf, QDSP_DATA,
+							 write_ptr_qdsp);
+			}
+		}
+	}
+}
+
+static void diag_print_mask_table(void)
+{
+/* Enable this to print mask table when updated */
+#ifdef MASK_DEBUG
+	int first;
+	int last;
+	uint8_t *ptr = driver->msg_masks;
+	int i = 0;
+
+	while (*(uint32_t *)(ptr + 4)) {
+		first = *(uint32_t *)ptr;
+		ptr += 4;
+		last = *(uint32_t *)ptr;
+		ptr += 4;
+		printk(KERN_INFO "SSID %d - %d\n", first, last);
+		for (i = 0 ; i <= last - first ; i++)
+			printk(KERN_INFO "MASK:%x\n", *((uint32_t *)ptr + i));
+		ptr += ((last - first) + 1)*4;
+
+	}
+#endif
+}
+
+static void diag_update_msg_mask(int start, int end , uint8_t *buf)
+{
+	int found = 0;
+	int first;
+	int last;
+	uint8_t *ptr = driver->msg_masks;
+	uint8_t *ptr_buffer_start = &(*(driver->msg_masks));
+	uint8_t *ptr_buffer_end = &(*(driver->msg_masks)) + MSG_MASK_SIZE;
+
+	mutex_lock(&driver->diagchar_mutex);
+	/* First SSID can be zero : So check that last is non-zero */
+
+	while (*(uint32_t *)(ptr + 4)) {
+		first = *(uint32_t *)ptr;
+		ptr += 4;
+		last = *(uint32_t *)ptr;
+		ptr += 4;
+		if (start >= first && start <= last) {
+			ptr += (start - first)*4;
+			if (end <= last)
+				if (CHK_OVERFLOW(ptr_buffer_start, ptr,
+						  ptr_buffer_end,
+						  (((end - start)+1)*4)))
+					memcpy(ptr, buf , ((end - start)+1)*4);
+				else
+					printk(KERN_CRIT "Not enough"
+							 " buffer space for"
+							 " MSG_MASK\n");
+			else
+				printk(KERN_INFO "Unable to copy"
+						 " mask change\n");
+
+			found = 1;
+			break;
+		} else {
+			ptr += ((last - first) + 1)*4;
+		}
+	}
+	/* Entry was not found - add new table */
+	if (!found) {
+		if (CHK_OVERFLOW(ptr_buffer_start, ptr, ptr_buffer_end,
+				  8 + ((end - start) + 1)*4)) {
+			memcpy(ptr, &(start) , 4);
+			ptr += 4;
+			memcpy(ptr, &(end), 4);
+			ptr += 4;
+			memcpy(ptr, buf , ((end - start) + 1)*4);
+		} else
+			printk(KERN_CRIT " Not enough buffer"
+					 " space for MSG_MASK\n");
+	}
+	mutex_unlock(&driver->diagchar_mutex);
+	diag_print_mask_table();
+
+}
+
+static void diag_update_event_mask(uint8_t *buf, int toggle, int num_bits)
+{
+	uint8_t *ptr = driver->event_masks;
+	uint8_t *temp = buf + 2;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (!toggle)
+		memset(ptr, 0 , EVENT_MASK_SIZE);
+	else
+		if (CHK_OVERFLOW(ptr, ptr,
+				 ptr+EVENT_MASK_SIZE,
+				  num_bits/8 + 1))
+			memcpy(ptr, temp , num_bits/8 + 1);
+		else
+			printk(KERN_CRIT "Not enough buffer space "
+					 "for EVENT_MASK\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_log_mask(int equip_id, uint8_t *buf, int num_items)
+{
+	uint8_t *temp = buf;
+	struct mask_info {
+		int equip_id;
+		int index;
+	};
+	int i = 0;
+	unsigned char *ptr_data;
+	int offset = 8*MAX_EQUIP_ID;
+	struct mask_info *ptr = (struct mask_info *)driver->log_masks;
+
+	mutex_lock(&driver->diagchar_mutex);
+	/* Check if we already know index of this equipment ID */
+	for (i = 0; i < MAX_EQUIP_ID; i++) {
+		if ((ptr->equip_id == equip_id) && (ptr->index != 0)) {
+			offset = ptr->index;
+			break;
+		}
+		if ((ptr->equip_id == 0) && (ptr->index == 0)) {
+			/*Reached a null entry */
+			ptr->equip_id = equip_id;
+			ptr->index = driver->log_masks_length;
+			offset = driver->log_masks_length;
+			driver->log_masks_length += ((num_items+7)/8);
+			break;
+		}
+		ptr++;
+	}
+	ptr_data = driver->log_masks + offset;
+	if (CHK_OVERFLOW(driver->log_masks, ptr_data, driver->log_masks
+					 + LOG_MASK_SIZE, (num_items+7)/8))
+		memcpy(ptr_data, temp , (num_items+7)/8);
+	else
+		printk(KERN_CRIT " Not enough buffer space for LOG_MASK\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+static void diag_update_pkt_buffer(unsigned char *buf)
+{
+	unsigned char *ptr = driver->pkt_buf;
+	unsigned char *temp = buf;
+
+	mutex_lock(&driver->diagchar_mutex);
+	if (CHK_OVERFLOW(ptr, ptr, ptr + PKT_SIZE, driver->pkt_length))
+		memcpy(ptr, temp , driver->pkt_length);
+	else
+		printk(KERN_CRIT " Not enough buffer space for PKT_RESP\n");
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_update_userspace_clients(unsigned int type)
+{
+	int i;
+
+	mutex_lock(&driver->diagchar_mutex);
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid != 0)
+			driver->data_ready[i] |= type;
+	wake_up_interruptible(&driver->wait_q);
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_update_sleeping_process(int process_id)
+{
+	int i;
+
+	mutex_lock(&driver->diagchar_mutex);
+	for (i = 0; i < driver->num_clients; i++)
+		if (driver->client_map[i].pid == process_id) {
+			driver->data_ready[i] |= PKT_TYPE;
+			break;
+		}
+	wake_up_interruptible(&driver->wait_q);
+	mutex_unlock(&driver->diagchar_mutex);
+}
+
+void diag_send_data(struct diag_master_table entry, unsigned char *buf,
+					 int len, int type)
+{
+	driver->pkt_length = len;
+	if (entry.process_id != NON_APPS_PROC && type != MODEM_DATA) {
+		diag_update_pkt_buffer(buf);
+		diag_update_sleeping_process(entry.process_id);
+	} else {
+		if (len > 0) {
+			if (entry.client_id == MODEM_PROC && driver->ch)
+				smd_write(driver->ch, buf, len);
+			else if (entry.client_id == QDSP_PROC &&
+							 driver->chqdsp)
+				smd_write(driver->chqdsp, buf, len);
+			else if (entry.client_id == WCNSS_PROC &&
+							 driver->ch_wcnss)
+				smd_write(driver->ch_wcnss, buf, len);
+			else
+				pr_alert("diag: incorrect channel");
+		}
+	}
+}
+
+static int diag_process_apps_pkt(unsigned char *buf, int len)
+{
+	uint16_t subsys_cmd_code;
+	int subsys_id, ssid_first, ssid_last, ssid_range;
+	int packet_type = 1, i, cmd_code;
+	unsigned char *temp = buf;
+	int data_type;
+#if defined(CONFIG_DIAG_OVER_USB)
+	int payload_length;
+	unsigned char *ptr;
+#endif
+
+	/* Check for registered clients and forward packet to apropriate proc */
+	cmd_code = (int)(*(char *)buf);
+	temp++;
+	subsys_id = (int)(*(char *)temp);
+	temp++;
+	subsys_cmd_code = *(uint16_t *)temp;
+	temp += 2;
+	data_type = APPS_DATA;
+	/* Dont send any command other than mode reset */
+	if (cpu_is_msm8960() && cmd_code == MODE_CMD) {
+		if (subsys_id != RESET_ID)
+			data_type = MODEM_DATA;
+	}
+
+	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
+	for (i = 0; i < diag_max_registration; i++) {
+		entry = driver->table[i];
+		if (entry.process_id != NO_PROCESS) {
+			if (entry.cmd_code == cmd_code && entry.subsys_id ==
+				 subsys_id && entry.cmd_code_lo <=
+							 subsys_cmd_code &&
+				  entry.cmd_code_hi >= subsys_cmd_code) {
+				diag_send_data(entry, buf, len, data_type);
+				packet_type = 0;
+			} else if (entry.cmd_code == 255
+				  && cmd_code == 75) {
+				if (entry.subsys_id ==
+					subsys_id &&
+				   entry.cmd_code_lo <=
+					subsys_cmd_code &&
+					 entry.cmd_code_hi >=
+					subsys_cmd_code) {
+					diag_send_data(entry, buf, len,
+								 data_type);
+					packet_type = 0;
+				}
+			} else if (entry.cmd_code == 255 &&
+				  entry.subsys_id == 255) {
+				if (entry.cmd_code_lo <=
+						 cmd_code &&
+						 entry.
+						cmd_code_hi >= cmd_code) {
+					diag_send_data(entry, buf, len,
+								 data_type);
+					packet_type = 0;
+				}
+			}
+		}
+	}
+	/* set event mask */
+	if (*buf == 0x82) {
+		buf += 4;
+		diag_update_event_mask(buf, 1, *(uint16_t *)buf);
+		diag_update_userspace_clients(EVENT_MASKS_TYPE);
+	}
+	/* event mask change */
+	else if ((*buf == 0x60) && (*(buf+1) == 0x0)) {
+		diag_update_event_mask(buf+1, 0, 0);
+		diag_update_userspace_clients(EVENT_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		/* Check for Apps Only 8960 */
+		if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) {
+			/* echo response back for apps only DIAG */
+			driver->apps_rsp_buf[0] = 0x60;
+			driver->apps_rsp_buf[1] = 0x0;
+			driver->apps_rsp_buf[2] = 0x0;
+			ENCODE_RSP_AND_SEND(2);
+			return 0;
+		}
+#endif
+	}
+	/* Set log masks */
+	else if (*buf == 0x73 && *(int *)(buf+4) == 3) {
+		buf += 8;
+		/* Read Equip ID and pass as first param below*/
+		diag_update_log_mask(*(int *)buf, buf+8, *(int *)(buf+4));
+		diag_update_userspace_clients(LOG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		/* Check for Apps Only 8960 */
+		if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) {
+			/* echo response back for Apps only DIAG */
+			driver->apps_rsp_buf[0] = 0x73;
+			*(int *)(driver->apps_rsp_buf + 4) = 0x3; /* op. ID */
+			*(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success */
+			payload_length = 8 + ((*(int *)(buf + 4)) + 7)/8;
+			for (i = 0; i < payload_length; i++)
+				*(int *)(driver->apps_rsp_buf+12+i) =
+								 *(buf+8+i);
+			ENCODE_RSP_AND_SEND(12 + payload_length - 1);
+			return 0;
+		}
+#endif
+	}
+	/* Check for set message mask  */
+	else if ((*buf == 0x7d) && (*(buf+1) == 0x4)) {
+		ssid_first = *(uint16_t *)(buf + 2);
+		ssid_last = *(uint16_t *)(buf + 4);
+		ssid_range = 4 * (ssid_last - ssid_first + 1);
+		diag_update_msg_mask(ssid_first, ssid_last , buf + 8);
+		diag_update_userspace_clients(MSG_MASKS_TYPE);
+#if defined(CONFIG_DIAG_OVER_USB)
+		if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)) {
+			/* echo response back for apps only DIAG */
+			for (i = 0; i < 8 + ssid_range; i++)
+				*(driver->apps_rsp_buf + i) = *(buf+i);
+			ENCODE_RSP_AND_SEND(8 + ssid_range - 1);
+			return 0;
+		}
+#endif
+	}
+#if defined(CONFIG_DIAG_OVER_USB)
+	/* Check for Apps Only 8960 & get event mask request */
+	else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)
+			  && *buf == 0x81) {
+		driver->apps_rsp_buf[0] = 0x81;
+		driver->apps_rsp_buf[1] = 0x0;
+		*(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0;
+		*(uint16_t *)(driver->apps_rsp_buf + 4) = EVENT_LAST_ID + 1;
+		for (i = 0; i < EVENT_LAST_ID/8 + 1; i++)
+			*(unsigned char *)(driver->apps_rsp_buf + 6 + i) = 0x0;
+		ENCODE_RSP_AND_SEND(6 + EVENT_LAST_ID/8);
+		return 0;
+	}
+	/* Get log ID range & Check for Apps Only 8960 */
+	else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)
+			  && (*buf == 0x73) && *(int *)(buf+4) == 1) {
+		driver->apps_rsp_buf[0] = 0x73;
+		*(int *)(driver->apps_rsp_buf + 4) = 0x1; /* operation ID */
+		*(int *)(driver->apps_rsp_buf + 8) = 0x0; /* success code */
+		*(int *)(driver->apps_rsp_buf + 12) = LOG_GET_ITEM_NUM(LOG_0);
+		*(int *)(driver->apps_rsp_buf + 16) = LOG_GET_ITEM_NUM(LOG_1);
+		*(int *)(driver->apps_rsp_buf + 20) = LOG_GET_ITEM_NUM(LOG_2);
+		*(int *)(driver->apps_rsp_buf + 24) = LOG_GET_ITEM_NUM(LOG_3);
+		*(int *)(driver->apps_rsp_buf + 28) = LOG_GET_ITEM_NUM(LOG_4);
+		*(int *)(driver->apps_rsp_buf + 32) = LOG_GET_ITEM_NUM(LOG_5);
+		*(int *)(driver->apps_rsp_buf + 36) = LOG_GET_ITEM_NUM(LOG_6);
+		*(int *)(driver->apps_rsp_buf + 40) = LOG_GET_ITEM_NUM(LOG_7);
+		*(int *)(driver->apps_rsp_buf + 44) = LOG_GET_ITEM_NUM(LOG_8);
+		*(int *)(driver->apps_rsp_buf + 48) = LOG_GET_ITEM_NUM(LOG_9);
+		*(int *)(driver->apps_rsp_buf + 52) = LOG_GET_ITEM_NUM(LOG_10);
+		*(int *)(driver->apps_rsp_buf + 56) = LOG_GET_ITEM_NUM(LOG_11);
+		*(int *)(driver->apps_rsp_buf + 60) = LOG_GET_ITEM_NUM(LOG_12);
+		*(int *)(driver->apps_rsp_buf + 64) = LOG_GET_ITEM_NUM(LOG_13);
+		*(int *)(driver->apps_rsp_buf + 68) = LOG_GET_ITEM_NUM(LOG_14);
+		*(int *)(driver->apps_rsp_buf + 72) = LOG_GET_ITEM_NUM(LOG_15);
+		ENCODE_RSP_AND_SEND(75);
+		return 0;
+	}
+	/* Respond to Get SSID Range request message */
+	else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)
+			 && (*buf == 0x7d) && (*(buf+1) == 0x1)) {
+		driver->apps_rsp_buf[0] = 0x7d;
+		driver->apps_rsp_buf[1] = 0x1;
+		driver->apps_rsp_buf[2] = 0x1;
+		driver->apps_rsp_buf[3] = 0x0;
+		*(int *)(driver->apps_rsp_buf + 4) = MSG_MASK_TBL_CNT;
+		*(uint16_t *)(driver->apps_rsp_buf + 8) = MSG_SSID_0;
+		*(uint16_t *)(driver->apps_rsp_buf + 10) = MSG_SSID_0_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 12) = MSG_SSID_1;
+		*(uint16_t *)(driver->apps_rsp_buf + 14) = MSG_SSID_1_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 16) = MSG_SSID_2;
+		*(uint16_t *)(driver->apps_rsp_buf + 18) = MSG_SSID_2_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 20) = MSG_SSID_3;
+		*(uint16_t *)(driver->apps_rsp_buf + 22) = MSG_SSID_3_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 24) = MSG_SSID_4;
+		*(uint16_t *)(driver->apps_rsp_buf + 26) = MSG_SSID_4_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 28) = MSG_SSID_5;
+		*(uint16_t *)(driver->apps_rsp_buf + 30) = MSG_SSID_5_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 32) = MSG_SSID_6;
+		*(uint16_t *)(driver->apps_rsp_buf + 34) = MSG_SSID_6_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 36) = MSG_SSID_7;
+		*(uint16_t *)(driver->apps_rsp_buf + 38) = MSG_SSID_7_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 40) = MSG_SSID_8;
+		*(uint16_t *)(driver->apps_rsp_buf + 42) = MSG_SSID_8_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 44) = MSG_SSID_9;
+		*(uint16_t *)(driver->apps_rsp_buf + 46) = MSG_SSID_9_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 48) = MSG_SSID_10;
+		*(uint16_t *)(driver->apps_rsp_buf + 50) = MSG_SSID_10_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 52) = MSG_SSID_11;
+		*(uint16_t *)(driver->apps_rsp_buf + 54) = MSG_SSID_11_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 56) = MSG_SSID_12;
+		*(uint16_t *)(driver->apps_rsp_buf + 58) = MSG_SSID_12_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 60) = MSG_SSID_13;
+		*(uint16_t *)(driver->apps_rsp_buf + 62) = MSG_SSID_13_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 64) = MSG_SSID_14;
+		*(uint16_t *)(driver->apps_rsp_buf + 66) = MSG_SSID_14_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 68) = MSG_SSID_15;
+		*(uint16_t *)(driver->apps_rsp_buf + 70) = MSG_SSID_15_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 72) = MSG_SSID_16;
+		*(uint16_t *)(driver->apps_rsp_buf + 74) = MSG_SSID_16_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 76) = MSG_SSID_17;
+		*(uint16_t *)(driver->apps_rsp_buf + 78) = MSG_SSID_17_LAST;
+		*(uint16_t *)(driver->apps_rsp_buf + 80) = MSG_SSID_18;
+		*(uint16_t *)(driver->apps_rsp_buf + 82) = MSG_SSID_18_LAST;
+		ENCODE_RSP_AND_SEND(83);
+		return 0;
+	}
+	/* Check for AO8960 Respond to Get Subsys Build mask */
+	else if (!(driver->ch) && (chk_config_get_id() == AO8960_TOOLS_ID)
+			 && (*buf == 0x7d) && (*(buf+1) == 0x2)) {
+		ssid_first = *(uint16_t *)(buf + 2);
+		ssid_last = *(uint16_t *)(buf + 4);
+		ssid_range = 4 * (ssid_last - ssid_first + 1);
+		/* frame response */
+		driver->apps_rsp_buf[0] = 0x7d;
+		driver->apps_rsp_buf[1] = 0x2;
+		*(uint16_t *)(driver->apps_rsp_buf + 2) = ssid_first;
+		*(uint16_t *)(driver->apps_rsp_buf + 4) = ssid_last;
+		driver->apps_rsp_buf[6] = 0x1;
+		driver->apps_rsp_buf[7] = 0x0;
+		ptr = driver->apps_rsp_buf + 8;
+		/* bld time masks */
+		switch (ssid_first) {
+		case MSG_SSID_0:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_0[i/4];
+			break;
+		case MSG_SSID_1:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_1[i/4];
+			break;
+		case MSG_SSID_2:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_2[i/4];
+			break;
+		case MSG_SSID_3:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_3[i/4];
+			break;
+		case MSG_SSID_4:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_4[i/4];
+			break;
+		case MSG_SSID_5:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_5[i/4];
+			break;
+		case MSG_SSID_6:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_6[i/4];
+			break;
+		case MSG_SSID_7:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_7[i/4];
+			break;
+		case MSG_SSID_8:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_8[i/4];
+			break;
+		case MSG_SSID_9:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_9[i/4];
+			break;
+		case MSG_SSID_10:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_10[i/4];
+			break;
+		case MSG_SSID_11:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_11[i/4];
+			break;
+		case MSG_SSID_12:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_12[i/4];
+			break;
+		case MSG_SSID_13:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_13[i/4];
+			break;
+		case MSG_SSID_14:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_14[i/4];
+			break;
+		case MSG_SSID_15:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_15[i/4];
+			break;
+		case MSG_SSID_16:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_16[i/4];
+			break;
+		case MSG_SSID_17:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_17[i/4];
+			break;
+		case MSG_SSID_18:
+			for (i = 0; i < ssid_range; i += 4)
+				*(int *)(ptr + i) = msg_bld_masks_18[i/4];
+			break;
+		}
+		ENCODE_RSP_AND_SEND(8 + ssid_range - 1);
+		return 0;
+	}
+	/* Check for download command */
+	else if ((cpu_is_msm8x60() || cpu_is_msm8960()) && (*buf == 0x3A)) {
+		/* send response back */
+		driver->apps_rsp_buf[0] = *buf;
+		ENCODE_RSP_AND_SEND(0);
+		msleep(5000);
+		/* call download API */
+		msm_set_restart_mode(RESTART_DLOAD);
+		printk(KERN_CRIT "diag: download mode set, Rebooting SoC..\n");
+		kernel_restart(NULL);
+		/* Not required, represents that command isnt sent to modem */
+		return 0;
+	}
+	 /* Check for ID for NO MODEM present */
+	else if (!(driver->ch)) {
+		/* Respond to polling for Apps only DIAG */
+		if ((*buf == 0x4b) && (*(buf+1) == 0x32) &&
+							 (*(buf+2) == 0x03)) {
+			for (i = 0; i < 3; i++)
+				driver->apps_rsp_buf[i] = *(buf+i);
+			for (i = 0; i < 13; i++)
+				driver->apps_rsp_buf[i+3] = 0;
+
+			ENCODE_RSP_AND_SEND(15);
+			return 0;
+		}
+		/* respond to 0x0 command */
+		else if (*buf == 0x00) {
+			for (i = 0; i < 55; i++)
+				driver->apps_rsp_buf[i] = 0;
+
+			ENCODE_RSP_AND_SEND(54);
+			return 0;
+		}
+		/* respond to 0x7c command */
+		else if (*buf == 0x7c) {
+			driver->apps_rsp_buf[0] = 0x7c;
+			for (i = 1; i < 8; i++)
+				driver->apps_rsp_buf[i] = 0;
+			/* Tools ID for APQ 8060 */
+			*(int *)(driver->apps_rsp_buf + 8) =
+							 chk_config_get_id();
+			*(unsigned char *)(driver->apps_rsp_buf + 12) = '\0';
+			*(unsigned char *)(driver->apps_rsp_buf + 13) = '\0';
+			ENCODE_RSP_AND_SEND(13);
+			return 0;
+		}
+	}
+#endif
+		return packet_type;
+}
+
+#ifdef CONFIG_DIAG_OVER_USB
+void diag_send_error_rsp(int index)
+{
+	int i;
+	driver->apps_rsp_buf[0] = 0x13; /* error code 13 */
+	for (i = 0; i < index; i++)
+		driver->apps_rsp_buf[i+1] = *(driver->hdlc_buf+i);
+	ENCODE_RSP_AND_SEND(index - 3);
+}
+#else
+static inline void diag_send_error_rsp(int index) {}
+#endif
+
+void diag_process_hdlc(void *data, unsigned len)
+{
+	struct diag_hdlc_decode_type hdlc;
+	int ret, type = 0;
+	pr_debug("diag: HDLC decode fn, len of data  %d\n", len);
+	hdlc.dest_ptr = driver->hdlc_buf;
+	hdlc.dest_size = USB_MAX_OUT_BUF;
+	hdlc.src_ptr = data;
+	hdlc.src_size = len;
+	hdlc.src_idx = 0;
+	hdlc.dest_idx = 0;
+	hdlc.escaping = 0;
+
+	ret = diag_hdlc_decode(&hdlc);
+
+	if (ret)
+		type = diag_process_apps_pkt(driver->hdlc_buf,
+							  hdlc.dest_idx - 3);
+	else if (driver->debug_flag) {
+		printk(KERN_ERR "Packet dropped due to bad HDLC coding/CRC"
+				" errors or partial packet received, packet"
+				" length = %d\n", len);
+		print_hex_dump(KERN_DEBUG, "Dropped Packet Data: ", 16, 1,
+					   DUMP_PREFIX_ADDRESS, data, len, 1);
+		driver->debug_flag = 0;
+	}
+	/* send error responses from APPS for Central Routing */
+	if (type == 1 && chk_config_get_id() == AO8960_TOOLS_ID) {
+		diag_send_error_rsp(hdlc.dest_idx);
+		type = 0;
+	}
+	/* implies this packet is NOT meant for apps */
+	if (!(driver->ch) && type == 1) {
+		if (chk_config_get_id() == AO8960_TOOLS_ID) {
+			diag_send_error_rsp(hdlc.dest_idx);
+		} else { /* APQ 8060, Let Q6 respond */
+			if (driver->chqdsp)
+				smd_write(driver->chqdsp, driver->hdlc_buf,
+						  hdlc.dest_idx - 3);
+		}
+		type = 0;
+	}
+
+#ifdef DIAG_DEBUG
+	pr_debug("diag: hdlc.dest_idx = %d", hdlc.dest_idx);
+	for (i = 0; i < hdlc.dest_idx; i++)
+		printk(KERN_DEBUG "\t%x", *(((unsigned char *)
+							driver->hdlc_buf)+i));
+#endif /* DIAG DEBUG */
+	/* ignore 2 bytes for CRC, one for 7E and send */
+	if ((driver->ch) && (ret) && (type) && (hdlc.dest_idx > 3)) {
+		APPEND_DEBUG('g');
+		smd_write(driver->ch, driver->hdlc_buf, hdlc.dest_idx - 3);
+		APPEND_DEBUG('h');
+#ifdef DIAG_DEBUG
+		printk(KERN_INFO "writing data to SMD, pkt length %d\n", len);
+		print_hex_dump(KERN_DEBUG, "Written Packet Data to SMD: ", 16,
+			       1, DUMP_PREFIX_ADDRESS, data, len, 1);
+#endif /* DIAG DEBUG */
+	}
+}
+
+#ifdef CONFIG_DIAG_OVER_USB
+#define N_LEGACY_WRITE	(driver->poolsize + 5) /* 2+1 for modem ; 2 for q6 */
+#define N_LEGACY_READ	1
+
+int diagfwd_connect(void)
+{
+	int err;
+
+	printk(KERN_DEBUG "diag: USB connected\n");
+	err = usb_diag_alloc_req(driver->legacy_ch, N_LEGACY_WRITE,
+			N_LEGACY_READ);
+	if (err)
+		printk(KERN_ERR "diag: unable to alloc USB req on legacy ch");
+
+	driver->usb_connected = 1;
+	driver->in_busy_1 = 0;
+	driver->in_busy_2 = 0;
+	driver->in_busy_qdsp_1 = 0;
+	driver->in_busy_qdsp_2 = 0;
+	driver->in_busy_wcnss = 0;
+
+	/* Poll SMD channels to check for data*/
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
+	/* Poll USB channel to check for data*/
+	queue_work(driver->diag_wq, &(driver->diag_read_work));
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa()) {
+		if (driver->mdm_ch && !IS_ERR(driver->mdm_ch))
+			diagfwd_connect_sdio();
+		else
+			printk(KERN_INFO "diag: No USB MDM ch");
+	}
+#endif
+	return 0;
+}
+
+int diagfwd_disconnect(void)
+{
+	printk(KERN_DEBUG "diag: USB disconnected\n");
+	driver->usb_connected = 0;
+	driver->in_busy_1 = 1;
+	driver->in_busy_2 = 1;
+	driver->in_busy_qdsp_1 = 1;
+	driver->in_busy_qdsp_2 = 1;
+	driver->in_busy_wcnss = 1;
+	driver->debug_flag = 1;
+	usb_diag_free_req(driver->legacy_ch);
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
+		if (driver->mdm_ch && !IS_ERR(driver->mdm_ch))
+			diagfwd_disconnect_sdio();
+#endif
+	/* TBD - notify and flow control SMD */
+	return 0;
+}
+
+int diagfwd_write_complete(struct diag_request *diag_write_ptr)
+{
+	unsigned char *buf = diag_write_ptr->buf;
+	/*Determine if the write complete is for data from modem/apps/q6 */
+	/* Need a context variable here instead */
+	if (buf == (void *)driver->buf_in_1) {
+		driver->in_busy_1 = 0;
+		APPEND_DEBUG('o');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	} else if (buf == (void *)driver->buf_in_2) {
+		driver->in_busy_2 = 0;
+		APPEND_DEBUG('O');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+	} else if (buf == (void *)driver->buf_in_qdsp_1) {
+		driver->in_busy_qdsp_1 = 0;
+		APPEND_DEBUG('p');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	} else if (buf == (void *)driver->buf_in_qdsp_2) {
+		driver->in_busy_qdsp_2 = 0;
+		APPEND_DEBUG('P');
+		queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+	} else if (buf == (void *)driver->buf_in_wcnss) {
+		driver->in_busy_wcnss = 0;
+		APPEND_DEBUG('R');
+		queue_work(driver->diag_wq,
+			 &(driver->diag_read_smd_wcnss_work));
+	}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	else if (buf == (void *)driver->buf_in_sdio)
+		if (machine_is_msm8x60_fusion() ||
+					 machine_is_msm8x60_fusn_ffa())
+			diagfwd_write_complete_sdio();
+		else
+			pr_err("diag: Incorrect buffer pointer while WRITE");
+#endif
+	else {
+		diagmem_free(driver, (unsigned char *)buf, POOL_TYPE_HDLC);
+		diagmem_free(driver, (unsigned char *)diag_write_ptr,
+						 POOL_TYPE_WRITE_STRUCT);
+		APPEND_DEBUG('q');
+	}
+	return 0;
+}
+
+int diagfwd_read_complete(struct diag_request *diag_read_ptr)
+{
+	int status = diag_read_ptr->status;
+	unsigned char *buf = diag_read_ptr->buf;
+
+	/* Determine if the read complete is for data on legacy/mdm ch */
+	if (buf == (void *)driver->usb_buf_out) {
+		driver->read_len_legacy = diag_read_ptr->actual;
+		APPEND_DEBUG('s');
+#ifdef DIAG_DEBUG
+		printk(KERN_INFO "read data from USB, pkt length %d",
+		    diag_read_ptr->actual);
+		print_hex_dump(KERN_DEBUG, "Read Packet Data from USB: ", 16, 1,
+		       DUMP_PREFIX_ADDRESS, diag_read_ptr->buf,
+		       diag_read_ptr->actual, 1);
+#endif /* DIAG DEBUG */
+		if (driver->logging_mode == USB_MODE) {
+			if (status != -ECONNRESET && status != -ESHUTDOWN)
+				queue_work(driver->diag_wq,
+					&(driver->diag_proc_hdlc_work));
+			else
+				queue_work(driver->diag_wq,
+						 &(driver->diag_read_work));
+		}
+	}
+#ifdef CONFIG_DIAG_SDIO_PIPE
+	else if (buf == (void *)driver->usb_buf_mdm_out) {
+		if (machine_is_msm8x60_fusion() ||
+					 machine_is_msm8x60_fusn_ffa()) {
+			driver->read_len_mdm = diag_read_ptr->actual;
+			diagfwd_read_complete_sdio();
+		} else
+			pr_err("diag: Incorrect buffer pointer while READ");
+	}
+#endif
+	else
+		printk(KERN_ERR "diag: Unknown buffer ptr from USB");
+
+	return 0;
+}
+
+void diag_read_work_fn(struct work_struct *work)
+{
+	APPEND_DEBUG('d');
+	driver->usb_read_ptr->buf = driver->usb_buf_out;
+	driver->usb_read_ptr->length = USB_MAX_OUT_BUF;
+	usb_diag_read(driver->legacy_ch, driver->usb_read_ptr);
+	APPEND_DEBUG('e');
+}
+
+void diag_process_hdlc_fn(struct work_struct *work)
+{
+	APPEND_DEBUG('D');
+	diag_process_hdlc(driver->usb_buf_out, driver->read_len_legacy);
+	diag_read_work_fn(work);
+	APPEND_DEBUG('E');
+}
+
+void diag_usb_legacy_notifier(void *priv, unsigned event,
+			struct diag_request *d_req)
+{
+	switch (event) {
+	case USB_DIAG_CONNECT:
+		diagfwd_connect();
+		break;
+	case USB_DIAG_DISCONNECT:
+		diagfwd_disconnect();
+		break;
+	case USB_DIAG_READ_DONE:
+		diagfwd_read_complete(d_req);
+		break;
+	case USB_DIAG_WRITE_DONE:
+		diagfwd_write_complete(d_req);
+		break;
+	default:
+		printk(KERN_ERR "Unknown event from USB diag\n");
+		break;
+	}
+}
+
+#endif /* DIAG OVER USB */
+
+static void diag_smd_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+}
+
+#if defined(CONFIG_MSM_N_WAY_SMD)
+static void diag_smd_qdsp_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_work));
+}
+#endif
+
+static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
+}
+
+static int diag_smd_probe(struct platform_device *pdev)
+{
+	int r = 0;
+
+	if (pdev->id == SMD_APPS_MODEM)
+		r = smd_open("DIAG", &driver->ch, driver, diag_smd_notify);
+#if defined(CONFIG_MSM_N_WAY_SMD)
+	if (pdev->id == SMD_APPS_QDSP)
+		r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP
+			, &driver->chqdsp, driver, diag_smd_qdsp_notify);
+#endif
+	if (pdev->id == SMD_APPS_WCNSS)
+		r = smd_named_open_on_edge("APPS_RIVA_DATA", SMD_APPS_WCNSS
+			, &driver->ch_wcnss, driver, diag_smd_wcnss_notify);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pr_debug("diag: open SMD port, Id = %d, r = %d\n", pdev->id, r);
+
+	return 0;
+}
+
+static int diagfwd_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_dev_pm_ops = {
+	.runtime_suspend = diagfwd_runtime_suspend,
+	.runtime_resume = diagfwd_runtime_resume,
+};
+
+static struct platform_driver msm_smd_ch1_driver = {
+
+	.probe = diag_smd_probe,
+	.driver = {
+		   .name = "DIAG",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_dev_pm_ops,
+		   },
+};
+
+static struct platform_driver diag_smd_lite_driver = {
+
+	.probe = diag_smd_probe,
+	.driver = {
+		   .name = "APPS_RIVA_DATA",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_dev_pm_ops,
+		   },
+};
+
+void diagfwd_init(void)
+{
+	diag_debug_buf_idx = 0;
+	driver->read_len_legacy = 0;
+	if (driver->buf_in_1 == NULL) {
+		driver->buf_in_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_1 == NULL)
+			goto err;
+	}
+	if (driver->buf_in_2 == NULL) {
+		driver->buf_in_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_2 == NULL)
+			goto err;
+	}
+	if (driver->buf_in_qdsp_1 == NULL) {
+		driver->buf_in_qdsp_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_1 == NULL)
+			goto err;
+	}
+	if (driver->buf_in_qdsp_2 == NULL) {
+		driver->buf_in_qdsp_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_2 == NULL)
+			goto err;
+	}
+	if (driver->buf_in_wcnss == NULL) {
+		driver->buf_in_wcnss = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_wcnss == NULL)
+			goto err;
+	}
+	if (driver->usb_buf_out  == NULL &&
+	     (driver->usb_buf_out = kzalloc(USB_MAX_OUT_BUF,
+					 GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->hdlc_buf == NULL
+	    && (driver->hdlc_buf = kzalloc(HDLC_MAX, GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->msg_masks == NULL
+	    && (driver->msg_masks = kzalloc(MSG_MASK_SIZE,
+					     GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->log_masks == NULL &&
+	    (driver->log_masks = kzalloc(LOG_MASK_SIZE, GFP_KERNEL)) == NULL)
+		goto err;
+	driver->log_masks_length = 8*MAX_EQUIP_ID;
+	if (driver->event_masks == NULL &&
+	    (driver->event_masks = kzalloc(EVENT_MASK_SIZE,
+					    GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->client_map == NULL &&
+	    (driver->client_map = kzalloc
+	     ((driver->num_clients) * sizeof(struct diag_client_map),
+		   GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->buf_tbl == NULL)
+			driver->buf_tbl = kzalloc(buf_tbl_size *
+			  sizeof(struct diag_write_device), GFP_KERNEL);
+	if (driver->buf_tbl == NULL)
+		goto err;
+	if (driver->data_ready == NULL &&
+	     (driver->data_ready = kzalloc(driver->num_clients * sizeof(int)
+							, GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->table == NULL &&
+	     (driver->table = kzalloc(diag_max_registration*
+		      sizeof(struct diag_master_table),
+		       GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->write_ptr_1 == NULL) {
+		driver->write_ptr_1 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_1 == NULL)
+			goto err;
+	}
+	if (driver->write_ptr_2 == NULL) {
+		driver->write_ptr_2 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_2 == NULL)
+			goto err;
+	}
+	if (driver->write_ptr_qdsp_1 == NULL) {
+		driver->write_ptr_qdsp_1 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_qdsp_1 == NULL)
+			goto err;
+	}
+	if (driver->write_ptr_qdsp_2 == NULL) {
+		driver->write_ptr_qdsp_2 = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_qdsp_2 == NULL)
+			goto err;
+	}
+	if (driver->write_ptr_wcnss == NULL) {
+		driver->write_ptr_wcnss = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_wcnss == NULL)
+			goto err;
+	}
+	if (driver->usb_read_ptr == NULL) {
+		driver->usb_read_ptr = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->usb_read_ptr == NULL)
+			goto err;
+	}
+	if (driver->pkt_buf == NULL &&
+	     (driver->pkt_buf = kzalloc(PKT_SIZE,
+			 GFP_KERNEL)) == NULL)
+		goto err;
+	if (driver->apps_rsp_buf == NULL) {
+			driver->apps_rsp_buf = kzalloc(500, GFP_KERNEL);
+		if (driver->apps_rsp_buf == NULL)
+			goto err;
+	}
+	driver->diag_wq = create_singlethread_workqueue("diag_wq");
+#ifdef CONFIG_DIAG_OVER_USB
+	INIT_WORK(&(driver->diag_proc_hdlc_work), diag_process_hdlc_fn);
+	INIT_WORK(&(driver->diag_read_work), diag_read_work_fn);
+	driver->legacy_ch = usb_diag_open(DIAG_LEGACY, driver,
+			diag_usb_legacy_notifier);
+	if (IS_ERR(driver->legacy_ch)) {
+		printk(KERN_ERR "Unable to open USB diag legacy channel\n");
+		goto err;
+	}
+#endif
+	platform_driver_register(&msm_smd_ch1_driver);
+	platform_driver_register(&diag_smd_lite_driver);
+
+	return;
+err:
+		pr_err("diag: Could not initialize diag buffers");
+		kfree(driver->buf_in_1);
+		kfree(driver->buf_in_2);
+		kfree(driver->buf_in_qdsp_1);
+		kfree(driver->buf_in_qdsp_2);
+		kfree(driver->buf_in_wcnss);
+		kfree(driver->usb_buf_out);
+		kfree(driver->hdlc_buf);
+		kfree(driver->msg_masks);
+		kfree(driver->log_masks);
+		kfree(driver->event_masks);
+		kfree(driver->client_map);
+		kfree(driver->buf_tbl);
+		kfree(driver->data_ready);
+		kfree(driver->table);
+		kfree(driver->pkt_buf);
+		kfree(driver->write_ptr_1);
+		kfree(driver->write_ptr_2);
+		kfree(driver->write_ptr_qdsp_1);
+		kfree(driver->write_ptr_qdsp_2);
+		kfree(driver->write_ptr_wcnss);
+		kfree(driver->usb_read_ptr);
+		kfree(driver->apps_rsp_buf);
+		if (driver->diag_wq)
+			destroy_workqueue(driver->diag_wq);
+}
+
+void diagfwd_exit(void)
+{
+	smd_close(driver->ch);
+	smd_close(driver->chqdsp);
+	smd_close(driver->ch_wcnss);
+	driver->ch = 0;		/* SMD can make this NULL */
+	driver->chqdsp = 0;
+	driver->ch_wcnss = 0;
+#ifdef CONFIG_DIAG_OVER_USB
+	if (driver->usb_connected)
+		usb_diag_free_req(driver->legacy_ch);
+	usb_diag_close(driver->legacy_ch);
+#endif
+	platform_driver_unregister(&msm_smd_ch1_driver);
+	platform_driver_unregister(&diag_smd_lite_driver);
+	kfree(driver->buf_in_1);
+	kfree(driver->buf_in_2);
+	kfree(driver->buf_in_qdsp_1);
+	kfree(driver->buf_in_qdsp_2);
+	kfree(driver->buf_in_wcnss);
+	kfree(driver->usb_buf_out);
+	kfree(driver->hdlc_buf);
+	kfree(driver->msg_masks);
+	kfree(driver->log_masks);
+	kfree(driver->event_masks);
+	kfree(driver->client_map);
+	kfree(driver->buf_tbl);
+	kfree(driver->data_ready);
+	kfree(driver->table);
+	kfree(driver->pkt_buf);
+	kfree(driver->write_ptr_1);
+	kfree(driver->write_ptr_2);
+	kfree(driver->write_ptr_qdsp_1);
+	kfree(driver->write_ptr_qdsp_2);
+	kfree(driver->write_ptr_wcnss);
+	kfree(driver->usb_read_ptr);
+	kfree(driver->apps_rsp_buf);
+	destroy_workqueue(driver->diag_wq);
+}
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
new file mode 100644
index 0000000..cc24cbc
--- /dev/null
+++ b/drivers/char/diag/diagfwd.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2008-2011, 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.
+ */
+
+#ifndef DIAGFWD_H
+#define DIAGFWD_H
+
+#define NO_PROCESS	0
+#define NON_APPS_PROC	-1
+
+void diagfwd_init(void);
+void diagfwd_exit(void);
+void diag_process_hdlc(void *data, unsigned len);
+void __diag_smd_send_req(void);
+void __diag_smd_qdsp_send_req(void);
+void __diag_smd_wcnss_send_req(void);
+void diag_usb_legacy_notifier(void *, unsigned, struct diag_request *);
+long diagchar_ioctl(struct file *, unsigned int, unsigned long);
+int diag_device_write(void *, int, struct diag_request *);
+int mask_request_validate(unsigned char mask_buf[]);
+int chk_config_get_id(void);
+/* State for diag forwarding */
+#ifdef CONFIG_DIAG_OVER_USB
+int diagfwd_connect(void);
+int diagfwd_disconnect(void);
+#endif
+extern int diag_debug_buf_idx;
+extern unsigned char diag_debug_buf[1024];
+
+#endif
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
new file mode 100644
index 0000000..45226ba
--- /dev/null
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2011, 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/diagchar.h>
+#include <linux/platform_device.h>
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_cntl.h"
+
+#define HDR_SIZ 8
+
+static void diag_smd_cntl_send_req(int proc_num)
+{
+	int data_len = 0, type = -1, count_bytes = 0, j, r;
+	struct bindpkt_params_per_process *pkt_params =
+		 kzalloc(sizeof(struct bindpkt_params_per_process), GFP_KERNEL);
+	struct diag_ctrl_msg *msg;
+	struct cmd_code_range *range;
+	struct bindpkt_params *temp;
+	void *buf = NULL;
+	smd_channel_t *smd_ch = NULL;
+
+	if (proc_num == MODEM_PROC) {
+		buf = driver->buf_in_cntl;
+		smd_ch = driver->ch_cntl;
+	} else if (proc_num == QDSP_PROC) {
+		buf = driver->buf_in_qdsp_cntl;
+		smd_ch = driver->chqdsp_cntl;
+	} else if (proc_num == WCNSS_PROC) {
+		buf = driver->buf_in_wcnss_cntl;
+		smd_ch = driver->ch_wcnss_cntl;
+	}
+
+	if (!smd_ch || !buf)
+		return;
+
+	r = smd_read_avail(smd_ch);
+	if (r > IN_BUF_SIZE) {
+		if (r < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD CNTL sending pkt upto %d bytes", r);
+			buf = krealloc(buf, r, GFP_KERNEL);
+		} else {
+			pr_err("diag: CNTL pkt > %d bytes", MAX_IN_BUF_SIZE);
+			kfree(pkt_params);
+			return;
+		}
+	}
+	if (buf && r > 0) {
+		smd_read(smd_ch, buf, r);
+		while (count_bytes + HDR_SIZ <= r) {
+			type = *(uint32_t *)(buf);
+			data_len = *(uint32_t *)(buf + 4);
+			count_bytes = count_bytes+HDR_SIZ+data_len;
+			if (type == DIAG_CTRL_MSG_REG && r >= count_bytes) {
+				msg = buf+HDR_SIZ;
+				range = buf+HDR_SIZ+
+						sizeof(struct diag_ctrl_msg);
+				pkt_params->count = msg->count_entries;
+				temp = kzalloc(pkt_params->count * sizeof(struct
+						 bindpkt_params), GFP_KERNEL);
+				for (j = 0; j < pkt_params->count; j++) {
+					temp->cmd_code = msg->cmd_code;
+					temp->subsys_id = msg->subsysid;
+					temp->client_id = proc_num;
+					temp->proc_id = proc_num;
+					temp->cmd_code_lo = range->cmd_code_lo;
+					temp->cmd_code_hi = range->cmd_code_hi;
+					range++;
+					temp++;
+				}
+				temp -= pkt_params->count;
+				pkt_params->params = temp;
+				diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
+						 (unsigned long)pkt_params);
+				kfree(temp);
+				buf = buf + HDR_SIZ + data_len;
+			}
+		}
+	}
+	kfree(pkt_params);
+}
+
+void diag_read_smd_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(MODEM_PROC);
+}
+
+void diag_read_smd_qdsp_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(QDSP_PROC);
+}
+
+void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *work)
+{
+	diag_smd_cntl_send_req(WCNSS_PROC);
+}
+
+static void diag_smd_cntl_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_cntl_work));
+}
+
+#if defined(CONFIG_MSM_N_WAY_SMD)
+static void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_qdsp_cntl_work));
+}
+#endif
+
+static void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_cntl_work));
+}
+
+static int diag_smd_cntl_probe(struct platform_device *pdev)
+{
+	int r = 0;
+
+	/* open control ports only on 8960 */
+	if (chk_config_get_id() == AO8960_TOOLS_ID) {
+		if (pdev->id == SMD_APPS_MODEM)
+			r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver,
+							diag_smd_cntl_notify);
+		if (pdev->id == SMD_APPS_QDSP)
+			r = smd_named_open_on_edge("DIAG_CNTL", SMD_APPS_QDSP
+				, &driver->chqdsp_cntl, driver,
+					 diag_smd_qdsp_cntl_notify);
+		if (pdev->id == SMD_APPS_WCNSS)
+			r = smd_named_open_on_edge("APPS_RIVA_CTRL",
+				SMD_APPS_WCNSS, &driver->ch_wcnss_cntl,
+					driver, diag_smd_wcnss_cntl_notify);
+		pr_debug("diag: open CNTL port, ID = %d,r = %d\n", pdev->id, r);
+	}
+	return 0;
+}
+
+static int diagfwd_cntl_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_cntl_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_cntl_dev_pm_ops = {
+	.runtime_suspend = diagfwd_cntl_runtime_suspend,
+	.runtime_resume = diagfwd_cntl_runtime_resume,
+};
+
+static struct platform_driver msm_smd_ch1_cntl_driver = {
+
+	.probe = diag_smd_cntl_probe,
+	.driver = {
+			.name = "DIAG_CNTL",
+			.owner = THIS_MODULE,
+			.pm   = &diagfwd_cntl_dev_pm_ops,
+		   },
+};
+
+static struct platform_driver diag_smd_lite_cntl_driver = {
+
+	.probe = diag_smd_cntl_probe,
+	.driver = {
+			.name = "APPS_RIVA_CTRL",
+			.owner = THIS_MODULE,
+			.pm   = &diagfwd_cntl_dev_pm_ops,
+		   },
+};
+
+void diagfwd_cntl_init(void)
+{
+	if (driver->buf_in_cntl == NULL) {
+		driver->buf_in_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_cntl == NULL)
+			goto err;
+	}
+	if (driver->buf_in_qdsp_cntl == NULL) {
+		driver->buf_in_qdsp_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_qdsp_cntl == NULL)
+			goto err;
+	}
+	if (driver->buf_in_wcnss_cntl == NULL) {
+		driver->buf_in_wcnss_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_wcnss_cntl == NULL)
+			goto err;
+	}
+	platform_driver_register(&msm_smd_ch1_cntl_driver);
+	platform_driver_register(&diag_smd_lite_cntl_driver);
+
+	return;
+err:
+		pr_err("diag: Could not initialize diag buffers");
+		kfree(driver->buf_in_cntl);
+		kfree(driver->buf_in_qdsp_cntl);
+		kfree(driver->buf_in_wcnss_cntl);
+}
+
+void diagfwd_cntl_exit(void)
+{
+	smd_close(driver->ch_cntl);
+	smd_close(driver->chqdsp_cntl);
+	smd_close(driver->ch_wcnss_cntl);
+	driver->ch_cntl = 0;
+	driver->chqdsp_cntl = 0;
+	driver->ch_wcnss_cntl = 0;
+	platform_driver_unregister(&msm_smd_ch1_cntl_driver);
+	platform_driver_unregister(&diag_smd_lite_cntl_driver);
+
+	kfree(driver->buf_in_cntl);
+	kfree(driver->buf_in_qdsp_cntl);
+	kfree(driver->buf_in_wcnss_cntl);
+}
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
new file mode 100644
index 0000000..542138d
--- /dev/null
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2011, 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.
+ */
+
+#ifndef DIAGFWD_CNTL_H
+#define DIAGFWD_CNTL_H
+
+#define DIAG_CTRL_MSG_REG 1	/* Message registration commands */
+
+struct cmd_code_range {
+	uint16_t cmd_code_lo;
+	uint16_t cmd_code_hi;
+	uint32_t data;
+};
+
+struct diag_ctrl_msg {
+	uint32_t version;
+	uint16_t cmd_code;
+	uint16_t subsysid;
+	uint16_t count_entries;
+	uint16_t port;
+};
+
+void diagfwd_cntl_init(void);
+void diagfwd_cntl_exit(void);
+void diag_read_smd_cntl_work_fn(struct work_struct *);
+void diag_read_smd_qdsp_cntl_work_fn(struct work_struct *);
+void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *);
+
+#endif
diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c
new file mode 100644
index 0000000..8d43286
--- /dev/null
+++ b/drivers/char/diag/diagfwd_sdio.c
@@ -0,0 +1,261 @@
+/* Copyright (c) 2011, 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/init.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <asm/current.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_sdio.h"
+
+void __diag_sdio_send_req(void)
+{
+	int r = 0;
+	void *buf = driver->buf_in_sdio;
+
+	if (driver->sdio_ch && (!driver->in_busy_sdio)) {
+		r = sdio_read_avail(driver->sdio_ch);
+
+		if (r > IN_BUF_SIZE) {
+			if (r < MAX_IN_BUF_SIZE) {
+				pr_err("diag: SDIO sending"
+					  " in packets more than %d bytes", r);
+				buf = krealloc(buf, r, GFP_KERNEL);
+			} else {
+				pr_err("diag: SDIO sending"
+			  " in packets more than %d bytes", MAX_IN_BUF_SIZE);
+				return;
+			}
+		}
+		if (r > 0) {
+			if (!buf)
+				printk(KERN_INFO "Out of diagmem for SDIO\n");
+			else {
+				APPEND_DEBUG('i');
+				sdio_read(driver->sdio_ch, buf, r);
+				APPEND_DEBUG('j');
+				driver->write_ptr_mdm->length = r;
+				driver->in_busy_sdio = 1;
+				diag_device_write(buf, SDIO_DATA,
+						 driver->write_ptr_mdm);
+			}
+		}
+	}
+}
+
+static void diag_read_sdio_work_fn(struct work_struct *work)
+{
+	__diag_sdio_send_req();
+}
+
+int diagfwd_connect_sdio(void)
+{
+	int err;
+
+	err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
+							 N_MDM_READ);
+	if (err)
+		printk(KERN_ERR "diag: unable to alloc USB req on mdm ch");
+
+	driver->in_busy_sdio = 0;
+
+	/* Poll USB channel to check for data*/
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	/* Poll SDIO channel to check for data*/
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
+	return 0;
+}
+
+int diagfwd_disconnect_sdio(void)
+{
+	driver->in_busy_sdio = 1;
+	usb_diag_free_req(driver->mdm_ch);
+	return 0;
+}
+
+int diagfwd_write_complete_sdio(void)
+{
+	driver->in_busy_sdio = 0;
+	APPEND_DEBUG('q');
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
+	return 0;
+}
+
+int diagfwd_read_complete_sdio(void)
+{
+	queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	return 0;
+}
+
+void diag_read_mdm_work_fn(struct work_struct *work)
+{
+	if (driver->sdio_ch) {
+		wait_event_interruptible(driver->wait_q, (sdio_write_avail
+				(driver->sdio_ch) >= driver->read_len_mdm));
+		if (driver->sdio_ch && driver->usb_buf_mdm_out &&
+						 (driver->read_len_mdm > 0))
+			sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out,
+							 driver->read_len_mdm);
+		APPEND_DEBUG('x');
+		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
+		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
+		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		APPEND_DEBUG('y');
+	}
+}
+
+static void diag_sdio_notify(void *ctxt, unsigned event)
+{
+	if (event == SDIO_EVENT_DATA_READ_AVAIL)
+		queue_work(driver->diag_sdio_wq,
+				 &(driver->diag_read_sdio_work));
+
+	if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
+		wake_up_interruptible(&driver->wait_q);
+}
+
+static int diag_sdio_probe(struct platform_device *pdev)
+{
+	int err;
+
+	err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
+							 diag_sdio_notify);
+	if (err)
+		printk(KERN_INFO "DIAG could not open SDIO channel");
+	else {
+		printk(KERN_INFO "DIAG opened SDIO channel");
+		queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
+	}
+
+	return err;
+}
+
+static int diag_sdio_remove(struct platform_device *pdev)
+{
+	queue_work(driver->diag_sdio_wq, &(driver->diag_remove_sdio_work));
+	return 0;
+}
+
+static void diag_remove_sdio_work_fn(struct work_struct *work)
+{
+	pr_debug("\n diag: sdio remove called");
+	/*Disable SDIO channel to prevent further read/write */
+	driver->sdio_ch = NULL;
+}
+
+static int diagfwd_sdio_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diagfwd_sdio_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diagfwd_sdio_dev_pm_ops = {
+	.runtime_suspend = diagfwd_sdio_runtime_suspend,
+	.runtime_resume = diagfwd_sdio_runtime_resume,
+};
+
+static struct platform_driver msm_sdio_ch_driver = {
+	.probe = diag_sdio_probe,
+	.remove = diag_sdio_remove,
+	.driver = {
+		   .name = "SDIO_DIAG",
+		   .owner = THIS_MODULE,
+		   .pm   = &diagfwd_sdio_dev_pm_ops,
+		   },
+};
+
+void diagfwd_sdio_init(void)
+{
+	int ret;
+
+	driver->read_len_mdm = 0;
+	if (driver->buf_in_sdio == NULL)
+		driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (driver->buf_in_sdio == NULL)
+			goto err;
+	if (driver->usb_buf_mdm_out  == NULL)
+		driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
+		if (driver->usb_buf_mdm_out == NULL)
+			goto err;
+	if (driver->write_ptr_mdm == NULL)
+		driver->write_ptr_mdm = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->write_ptr_mdm == NULL)
+			goto err;
+	if (driver->usb_read_mdm_ptr == NULL)
+		driver->usb_read_mdm_ptr = kzalloc(
+			sizeof(struct diag_request), GFP_KERNEL);
+		if (driver->usb_read_mdm_ptr == NULL)
+			goto err;
+	driver->diag_sdio_wq = create_singlethread_workqueue("diag_sdio_wq");
+#ifdef CONFIG_DIAG_OVER_USB
+	driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
+			diag_usb_legacy_notifier);
+	if (IS_ERR(driver->mdm_ch)) {
+		printk(KERN_ERR "Unable to open USB diag MDM channel\n");
+		goto err;
+	}
+	INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
+#endif
+	INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
+	INIT_WORK(&(driver->diag_remove_sdio_work), diag_remove_sdio_work_fn);
+	ret = platform_driver_register(&msm_sdio_ch_driver);
+	if (ret)
+		printk(KERN_INFO "DIAG could not register SDIO device");
+	else
+		printk(KERN_INFO "DIAG registered SDIO device");
+
+	return;
+err:
+		printk(KERN_INFO "\n Could not initialize diag buf for SDIO");
+		kfree(driver->buf_in_sdio);
+		kfree(driver->usb_buf_mdm_out);
+		kfree(driver->write_ptr_mdm);
+		kfree(driver->usb_read_mdm_ptr);
+		if (driver->diag_sdio_wq)
+			destroy_workqueue(driver->diag_sdio_wq);
+}
+
+void diagfwd_sdio_exit(void)
+{
+#ifdef CONFIG_DIAG_OVER_USB
+	if (driver->usb_connected)
+		usb_diag_free_req(driver->mdm_ch);
+#endif
+	platform_driver_unregister(&msm_sdio_ch_driver);
+#ifdef CONFIG_DIAG_OVER_USB
+	usb_diag_close(driver->mdm_ch);
+#endif
+	kfree(driver->buf_in_sdio);
+	kfree(driver->usb_buf_mdm_out);
+	kfree(driver->write_ptr_mdm);
+	kfree(driver->usb_read_mdm_ptr);
+	destroy_workqueue(driver->diag_sdio_wq);
+}
diff --git a/drivers/char/diag/diagfwd_sdio.h b/drivers/char/diag/diagfwd_sdio.h
new file mode 100644
index 0000000..40982c3
--- /dev/null
+++ b/drivers/char/diag/diagfwd_sdio.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2011, 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.
+ */
+
+#ifndef DIAGFWD_SDIO_H
+#define DIAGFWD_SDIO_H
+
+#include <mach/sdio_al.h>
+#define N_MDM_WRITE	1 /* Upgrade to 2 with ping pong buffer */
+#define N_MDM_READ	1
+
+void diagfwd_sdio_init(void);
+void diagfwd_sdio_exit(void);
+int diagfwd_connect_sdio(void);
+int diagfwd_disconnect_sdio(void);
+int diagfwd_read_complete_sdio(void);
+int diagfwd_write_complete_sdio(void);
+
+#endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
new file mode 100644
index 0000000..0b5c27a
--- /dev/null
+++ b/drivers/char/diag/diagmem.c
@@ -0,0 +1,145 @@
+/* Copyright (c) 2008-2010, 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/init.h>
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <asm/atomic.h>
+#include "diagchar.h"
+
+void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
+{
+	void *buf = NULL;
+
+	if (pool_type == POOL_TYPE_COPY) {
+		if (driver->diagpool) {
+			mutex_lock(&driver->diagmem_mutex);
+			if (driver->count < driver->poolsize) {
+				atomic_add(1, (atomic_t *)&driver->count);
+				buf = mempool_alloc(driver->diagpool,
+								 GFP_ATOMIC);
+			}
+			mutex_unlock(&driver->diagmem_mutex);
+		}
+	} else if (pool_type == POOL_TYPE_HDLC) {
+		if (driver->diag_hdlc_pool) {
+			if (driver->count_hdlc_pool < driver->poolsize_hdlc) {
+				atomic_add(1,
+					 (atomic_t *)&driver->count_hdlc_pool);
+				buf = mempool_alloc(driver->diag_hdlc_pool,
+								 GFP_ATOMIC);
+			}
+		}
+	} else if (pool_type == POOL_TYPE_WRITE_STRUCT) {
+		if (driver->diag_write_struct_pool) {
+			if (driver->count_write_struct_pool <
+					 driver->poolsize_write_struct) {
+				atomic_add(1,
+				 (atomic_t *)&driver->count_write_struct_pool);
+				buf = mempool_alloc(
+				driver->diag_write_struct_pool, GFP_ATOMIC);
+			}
+		}
+	}
+	return buf;
+}
+
+void diagmem_exit(struct diagchar_dev *driver, int pool_type)
+{
+	if (driver->diagpool) {
+		if (driver->count == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diagpool);
+			driver->diagpool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy COPY mempool");
+		}
+
+	if (driver->diag_hdlc_pool) {
+		if (driver->count_hdlc_pool == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diag_hdlc_pool);
+			driver->diag_hdlc_pool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy HDLC mempool");
+		}
+
+	if (driver->diag_write_struct_pool) {
+		/* Free up struct pool ONLY if there are no outstanding
+		transactions(aggregation buffer) with USB */
+		if (driver->count_write_struct_pool == 0 &&
+		 driver->count_hdlc_pool == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diag_write_struct_pool);
+			driver->diag_write_struct_pool = NULL;
+		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
+			printk(KERN_ALERT "Unable to destroy STRUCT mempool");
+		}
+}
+
+void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type)
+{
+	if (pool_type == POOL_TYPE_COPY) {
+		if (driver->diagpool != NULL && driver->count > 0) {
+			mempool_free(buf, driver->diagpool);
+			atomic_add(-1, (atomic_t *)&driver->count);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+	       "mempool memory which is already free %d", driver->count);
+	} else if (pool_type == POOL_TYPE_HDLC) {
+		if (driver->diag_hdlc_pool != NULL &&
+			 driver->count_hdlc_pool > 0) {
+			mempool_free(buf, driver->diag_hdlc_pool);
+			atomic_add(-1, (atomic_t *)&driver->count_hdlc_pool);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+	"HDLC mempool which is already free %d ", driver->count_hdlc_pool);
+	} else if (pool_type == POOL_TYPE_WRITE_STRUCT) {
+		if (driver->diag_write_struct_pool != NULL &&
+			 driver->count_write_struct_pool > 0) {
+			mempool_free(buf, driver->diag_write_struct_pool);
+			atomic_add(-1,
+				 (atomic_t *)&driver->count_write_struct_pool);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver "
+			   "USB structure mempool which is already free %d ",
+				    driver->count_write_struct_pool);
+	}
+
+	diagmem_exit(driver, pool_type);
+}
+
+void diagmem_init(struct diagchar_dev *driver)
+{
+	mutex_init(&driver->diagmem_mutex);
+
+	if (driver->count == 0)
+		driver->diagpool = mempool_create_kmalloc_pool(
+					driver->poolsize, driver->itemsize);
+
+	if (driver->count_hdlc_pool == 0)
+		driver->diag_hdlc_pool = mempool_create_kmalloc_pool(
+				driver->poolsize_hdlc, driver->itemsize_hdlc);
+
+	if (driver->count_write_struct_pool == 0)
+		driver->diag_write_struct_pool = mempool_create_kmalloc_pool(
+		driver->poolsize_write_struct, driver->itemsize_write_struct);
+
+	if (!driver->diagpool)
+		printk(KERN_INFO "Cannot allocate diag mempool\n");
+
+	if (!driver->diag_hdlc_pool)
+		printk(KERN_INFO "Cannot allocate diag HDLC mempool\n");
+
+	if (!driver->diag_write_struct_pool)
+		printk(KERN_INFO "Cannot allocate diag USB struct mempool\n");
+}
+
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
new file mode 100644
index 0000000..43829ae
--- /dev/null
+++ b/drivers/char/diag/diagmem.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2008-2010, 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.
+ */
+
+#ifndef DIAGMEM_H
+#define DIAGMEM_H
+#include "diagchar.h"
+
+void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type);
+void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
+void diagmem_init(struct diagchar_dev *driver);
+void diagmem_exit(struct diagchar_dev *driver, int pool_type);
+
+#endif
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index a60043b..127bdc6 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -210,3 +210,16 @@
 	  module will be called picoxcell-rng.
 
 	  If unsure, say Y.
+
+config HW_RANDOM_MSM
+	tristate "Qualcomm MSM Random Number Generator support"
+	depends on HW_RANDOM && ARCH_MSM
+	default n
+	---help---
+	  This driver provides kernel-side support for the Random Number
+	  Generator hardware found on Qualcomm MSM SoCs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called msm_rng.
+
+	  If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 3db4eb8..d0c065d 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -20,3 +20,4 @@
 obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
 obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
 obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
+obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
new file mode 100644
index 0000000..7051bf9
--- /dev/null
+++ b/drivers/char/hw_random/msm_rng.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2011, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+
+#define DRIVER_NAME "msm_rng"
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT_OFFSET    0x0000
+#define PRNG_STATUS_OFFSET	0x0004
+#define PRNG_LFSR_CFG_OFFSET	0x0100
+#define PRNG_CONFIG_OFFSET	0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK	0xFFFF0000
+#define PRNG_LFSR_CFG_CLOCKS	0x0000DDDD
+#define PRNG_CONFIG_MASK	0xFFFFFFFD
+#define PRNG_CONFIG_ENABLE	0x00000002
+
+#define MAX_HW_FIFO_DEPTH 16                     /* FIFO is 16 words deep */
+#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide  */
+
+struct msm_rng_device {
+	struct platform_device *pdev;
+	void __iomem *base;
+	struct clk *prng_clk;
+};
+
+static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+	struct msm_rng_device *msm_rng_dev;
+	struct platform_device *pdev;
+	void __iomem *base;
+	size_t maxsize;
+	size_t currsize = 0;
+	unsigned long val;
+	unsigned long *retdata = data;
+	int ret;
+
+	msm_rng_dev = (struct msm_rng_device *)rng->priv;
+	pdev = msm_rng_dev->pdev;
+	base = msm_rng_dev->base;
+
+	/* calculate max size bytes to transfer back to caller */
+	maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
+
+	/* no room for word data */
+	if (maxsize < 4)
+		return 0;
+
+	/* enable PRNG clock */
+	ret = clk_enable(msm_rng_dev->prng_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable clock in callback\n");
+		return 0;
+	}
+
+	/* read random data from h/w */
+	do {
+		/* check status bit if data is available */
+		if (!(readl(base + PRNG_STATUS_OFFSET) & 0x00000001))
+			break;	/* no data to read so just bail */
+
+		/* read FIFO */
+		val = readl(base + PRNG_DATA_OUT_OFFSET);
+		if (!val)
+			break;	/* no data to read so just bail */
+
+		/* write data back to callers pointer */
+		*(retdata++) = val;
+		currsize += 4;
+
+		/* make sure we stay on 32bit boundary */
+		if ((maxsize - currsize) < 4)
+			break;
+	} while (currsize < maxsize);
+
+	/* vote to turn off clock */
+	clk_disable(msm_rng_dev->prng_clk);
+
+	return currsize;
+}
+
+static struct hwrng msm_rng = {
+	.name = DRIVER_NAME,
+	.read = msm_rng_read,
+};
+
+static int __devinit msm_rng_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct msm_rng_device *msm_rng_dev = NULL;
+	void __iomem *base = NULL;
+	int error = 0;
+	unsigned long val;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "invalid address\n");
+		error = -EFAULT;
+		goto err_exit;
+	}
+
+	msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
+	if (!msm_rng_dev) {
+		dev_err(&pdev->dev, "cannot allocate memory\n");
+		error = -ENOMEM;
+		goto err_exit;
+	}
+
+	base = ioremap(res->start, resource_size(res));
+	if (!base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		error = -ENOMEM;
+		goto err_iomap;
+	}
+	msm_rng_dev->base = base;
+
+	/* create a handle for clock control */
+	msm_rng_dev->prng_clk = clk_get(NULL, "prng_clk");
+	if (IS_ERR(msm_rng_dev->prng_clk)) {
+		dev_err(&pdev->dev, "failed to register clock source\n");
+		error = -EPERM;
+		goto err_clk_get;
+	}
+
+	/* save away pdev and register driver data */
+	msm_rng_dev->pdev = pdev;
+	platform_set_drvdata(pdev, msm_rng_dev);
+
+	ret = clk_enable(msm_rng_dev->prng_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable clock in probe\n");
+		error = -EPERM;
+		goto err_clk_enable;
+	}
+
+	/* enable PRNG h/w (this may not work since XPU protect may be enabled
+	 * elsewhere which case then the hardware should have already been set
+	 * up)
+	 */
+	val = readl(base + PRNG_LFSR_CFG_OFFSET) & PRNG_LFSR_CFG_MASK;
+	val |= PRNG_LFSR_CFG_CLOCKS;
+	writel(val, base + PRNG_LFSR_CFG_OFFSET);
+
+	val = readl(base + PRNG_CONFIG_OFFSET) & PRNG_CONFIG_MASK;
+	val |= PRNG_CONFIG_ENABLE;
+	writel(val, base + PRNG_CONFIG_OFFSET);
+
+	clk_disable(msm_rng_dev->prng_clk);
+
+	/* register with hwrng framework */
+	msm_rng.priv = (unsigned long) msm_rng_dev;
+	error = hwrng_register(&msm_rng);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register hwrng\n");
+		error = -EPERM;
+		goto err_hw_register;
+	}
+
+	return 0;
+
+err_hw_register:
+err_clk_enable:
+	clk_put(msm_rng_dev->prng_clk);
+err_clk_get:
+	iounmap(msm_rng_dev->base);
+err_iomap:
+	kfree(msm_rng_dev);
+err_exit:
+	return error;
+}
+
+static int __devexit msm_rng_remove(struct platform_device *pdev)
+{
+	struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
+
+	hwrng_unregister(&msm_rng);
+	clk_put(msm_rng_dev->prng_clk);
+	iounmap(msm_rng_dev->base);
+	platform_set_drvdata(pdev, NULL);
+	kfree(msm_rng_dev);
+	return 0;
+}
+
+static struct platform_driver rng_driver = {
+	.probe      = msm_rng_probe,
+	.remove     = __devexit_p(msm_rng_remove),
+	.driver     = {
+		.name   = DRIVER_NAME,
+		.owner  = THIS_MODULE,
+	}
+};
+
+static int __init msm_rng_init(void)
+{
+	return platform_driver_register(&rng_driver);
+}
+
+module_init(msm_rng_init);
+
+static void __exit msm_rng_exit(void)
+{
+	platform_driver_unregister(&rng_driver);
+}
+
+module_exit(msm_rng_exit);
+
+MODULE_AUTHOR("Code Aurora Forum");
+MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
new file mode 100644
index 0000000..e7c790d
--- /dev/null
+++ b/drivers/char/msm_rotator.c
@@ -0,0 +1,1523 @@
+/* Copyright (c) 2009-2010, 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/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_rotator.h>
+#include <linux/io.h>
+#include <mach/msm_rotator_imem.h>
+#include <linux/ktime.h>
+#include <linux/workqueue.h>
+#include <linux/file.h>
+#include <linux/major.h>
+#include <linux/regulator/consumer.h>
+
+#define DRIVER_NAME "msm_rotator"
+
+#define MSM_ROTATOR_BASE (msm_rotator_dev->io_base)
+#define MSM_ROTATOR_INTR_ENABLE			(MSM_ROTATOR_BASE+0x0020)
+#define MSM_ROTATOR_INTR_STATUS			(MSM_ROTATOR_BASE+0x0024)
+#define MSM_ROTATOR_INTR_CLEAR			(MSM_ROTATOR_BASE+0x0028)
+#define MSM_ROTATOR_START			(MSM_ROTATOR_BASE+0x0030)
+#define MSM_ROTATOR_MAX_BURST_SIZE		(MSM_ROTATOR_BASE+0x0050)
+#define MSM_ROTATOR_HW_VERSION			(MSM_ROTATOR_BASE+0x0070)
+#define MSM_ROTATOR_SRC_SIZE			(MSM_ROTATOR_BASE+0x1108)
+#define MSM_ROTATOR_SRCP0_ADDR			(MSM_ROTATOR_BASE+0x110c)
+#define MSM_ROTATOR_SRCP1_ADDR			(MSM_ROTATOR_BASE+0x1110)
+#define MSM_ROTATOR_SRC_YSTRIDE1		(MSM_ROTATOR_BASE+0x111c)
+#define MSM_ROTATOR_SRC_YSTRIDE2		(MSM_ROTATOR_BASE+0x1120)
+#define MSM_ROTATOR_SRC_FORMAT			(MSM_ROTATOR_BASE+0x1124)
+#define MSM_ROTATOR_SRC_UNPACK_PATTERN1		(MSM_ROTATOR_BASE+0x1128)
+#define MSM_ROTATOR_SUB_BLOCK_CFG		(MSM_ROTATOR_BASE+0x1138)
+#define MSM_ROTATOR_OUT_PACK_PATTERN1		(MSM_ROTATOR_BASE+0x1154)
+#define MSM_ROTATOR_OUTP0_ADDR			(MSM_ROTATOR_BASE+0x1168)
+#define MSM_ROTATOR_OUTP1_ADDR			(MSM_ROTATOR_BASE+0x116c)
+#define MSM_ROTATOR_OUT_YSTRIDE1		(MSM_ROTATOR_BASE+0x1178)
+#define MSM_ROTATOR_OUT_YSTRIDE2		(MSM_ROTATOR_BASE+0x117c)
+#define MSM_ROTATOR_SRC_XY			(MSM_ROTATOR_BASE+0x1200)
+#define MSM_ROTATOR_SRC_IMAGE_SIZE		(MSM_ROTATOR_BASE+0x1208)
+
+#define MSM_ROTATOR_MAX_ROT	0x07
+#define MSM_ROTATOR_MAX_H	0x1fff
+#define MSM_ROTATOR_MAX_W	0x1fff
+
+/* from lsb to msb */
+#define GET_PACK_PATTERN(a, x, y, z, bit) \
+			(((a)<<((bit)*3))|((x)<<((bit)*2))|((y)<<(bit))|(z))
+#define CLR_G 0x0
+#define CLR_B 0x1
+#define CLR_R 0x2
+#define CLR_ALPHA 0x3
+
+#define CLR_Y  CLR_G
+#define CLR_CB CLR_B
+#define CLR_CR CLR_R
+
+#define ROTATIONS_TO_BITMASK(r) ((((r) & MDP_ROT_90) ? 1 : 0)  | \
+				 (((r) & MDP_FLIP_LR) ? 2 : 0) | \
+				 (((r) & MDP_FLIP_UD) ? 4 : 0))
+
+#define IMEM_NO_OWNER -1;
+
+#define MAX_SESSIONS 16
+#define INVALID_SESSION -1
+#define VERSION_KEY_MASK 0xFFFFFF00
+
+struct tile_parm {
+	unsigned int width;  /* tile's width */
+	unsigned int height; /* tile's height */
+	unsigned int row_tile_w; /* tiles per row's width */
+	unsigned int row_tile_h; /* tiles per row's height */
+};
+
+struct msm_rotator_dev {
+	void __iomem *io_base;
+	int irq;
+	struct msm_rotator_img_info *img_info[MAX_SESSIONS];
+	struct clk *core_clk;
+	int pid_list[MAX_SESSIONS];
+	struct clk *pclk;
+	struct clk *axi_clk;
+	int rot_clk_state;
+	struct regulator *regulator;
+	struct delayed_work rot_clk_work;
+	struct clk *imem_clk;
+	int imem_clk_state;
+	struct delayed_work imem_clk_work;
+	struct platform_device *pdev;
+	struct cdev cdev;
+	struct device *device;
+	struct class *class;
+	dev_t dev_num;
+	int processing;
+	int last_session_idx;
+	struct mutex rotator_lock;
+	struct mutex imem_lock;
+	int imem_owner;
+	wait_queue_head_t wq;
+};
+
+#define chroma_addr(start, w, h, bpp) ((start) + ((h) * (w) * (bpp)))
+
+#define COMPONENT_5BITS 1
+#define COMPONENT_6BITS 2
+#define COMPONENT_8BITS 3
+
+static struct msm_rotator_dev *msm_rotator_dev;
+
+enum {
+	CLK_EN,
+	CLK_DIS,
+	CLK_SUSPEND,
+};
+
+int msm_rotator_imem_allocate(int requestor)
+{
+	int rc = 0;
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	switch (requestor) {
+	case ROTATOR_REQUEST:
+		if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
+			msm_rotator_dev->imem_owner = ROTATOR_REQUEST;
+			rc = 1;
+		} else
+			rc = 0;
+		break;
+	case JPEG_REQUEST:
+		mutex_lock(&msm_rotator_dev->imem_lock);
+		msm_rotator_dev->imem_owner = JPEG_REQUEST;
+		rc = 1;
+		break;
+	default:
+		rc = 0;
+	}
+#else
+	if (requestor == JPEG_REQUEST)
+		rc = 1;
+#endif
+	if (rc == 1) {
+		cancel_delayed_work(&msm_rotator_dev->imem_clk_work);
+		if (msm_rotator_dev->imem_clk_state != CLK_EN
+			&& msm_rotator_dev->imem_clk) {
+			clk_enable(msm_rotator_dev->imem_clk);
+			msm_rotator_dev->imem_clk_state = CLK_EN;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_rotator_imem_allocate);
+
+void msm_rotator_imem_free(int requestor)
+{
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_owner == requestor) {
+		schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
+		mutex_unlock(&msm_rotator_dev->imem_lock);
+	}
+#else
+	if (requestor == JPEG_REQUEST)
+		schedule_delayed_work(&msm_rotator_dev->imem_clk_work, HZ);
+#endif
+}
+EXPORT_SYMBOL(msm_rotator_imem_free);
+
+static void msm_rotator_imem_clk_work_f(struct work_struct *work)
+{
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (mutex_trylock(&msm_rotator_dev->imem_lock)) {
+		if (msm_rotator_dev->imem_clk_state == CLK_EN
+		     && msm_rotator_dev->imem_clk) {
+			clk_disable(msm_rotator_dev->imem_clk);
+			msm_rotator_dev->imem_clk_state = CLK_DIS;
+		} else if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND)
+			msm_rotator_dev->imem_clk_state = CLK_DIS;
+		mutex_unlock(&msm_rotator_dev->imem_lock);
+	}
+#endif
+}
+
+/* enable clocks needed by rotator block */
+static void enable_rot_clks(void)
+{
+	if (msm_rotator_dev->regulator)
+		regulator_enable(msm_rotator_dev->regulator);
+	if (msm_rotator_dev->core_clk != NULL)
+		clk_enable(msm_rotator_dev->core_clk);
+	if (msm_rotator_dev->pclk != NULL)
+		clk_enable(msm_rotator_dev->pclk);
+	if (msm_rotator_dev->axi_clk != NULL)
+		clk_enable(msm_rotator_dev->axi_clk);
+}
+
+/* disable clocks needed by rotator block */
+static void disable_rot_clks(void)
+{
+	if (msm_rotator_dev->core_clk != NULL)
+		clk_disable(msm_rotator_dev->core_clk);
+	if (msm_rotator_dev->pclk != NULL)
+		clk_disable(msm_rotator_dev->pclk);
+	if (msm_rotator_dev->axi_clk != NULL)
+		clk_disable(msm_rotator_dev->axi_clk);
+	if (msm_rotator_dev->regulator)
+		regulator_disable(msm_rotator_dev->regulator);
+}
+
+static void msm_rotator_rot_clk_work_f(struct work_struct *work)
+{
+	if (mutex_trylock(&msm_rotator_dev->rotator_lock)) {
+		if (msm_rotator_dev->rot_clk_state == CLK_EN) {
+			disable_rot_clks();
+			msm_rotator_dev->rot_clk_state = CLK_DIS;
+		} else if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND)
+			msm_rotator_dev->rot_clk_state = CLK_DIS;
+		mutex_unlock(&msm_rotator_dev->rotator_lock);
+	}
+}
+
+static irqreturn_t msm_rotator_isr(int irq, void *dev_id)
+{
+	if (msm_rotator_dev->processing) {
+		msm_rotator_dev->processing = 0;
+		wake_up(&msm_rotator_dev->wq);
+	} else
+		printk(KERN_WARNING "%s: unexpected interrupt\n", DRIVER_NAME);
+
+	return IRQ_HANDLED;
+}
+
+static int get_bpp(int format)
+{
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+		return 2;
+
+	case MDP_XRGB_8888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+		return 4;
+
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		return 1;
+
+	case MDP_RGB_888:
+		return 3;
+
+	case MDP_YCRYCB_H2V1:
+		return 2;/* YCrYCb interleave */
+
+	case MDP_Y_CRCB_H2V1:
+	case MDP_Y_CBCR_H2V1:
+		return 1;
+
+	default:
+		return -1;
+	}
+
+}
+
+static int msm_rotator_ycxcx_h2v1(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned int in_chroma_paddr,
+				  unsigned int out_chroma_paddr)
+{
+	int bpp;
+	unsigned int in_chr_addr, out_chr_addr;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	if (!in_chroma_paddr) {
+		in_chr_addr = chroma_addr(in_paddr, info->src.width,
+				info->src.height,
+				bpp);
+	} else
+		in_chr_addr = in_chroma_paddr;
+
+	if (!out_chroma_paddr) {
+		out_chr_addr = chroma_addr(out_paddr, info->dst.width,
+				info->dst.height,
+				bpp);
+	} else
+		out_chr_addr = out_chroma_paddr;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_chr_addr, MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chr_addr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width |
+			  info->src.width << 16,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+		if (info->rotations & MDP_ROT_90)
+			iowrite32(info->dst.width |
+				  info->dst.width*2 << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+		else
+			iowrite32(info->dst.width |
+				  info->dst.width << 16,
+				  MSM_ROTATOR_OUT_YSTRIDE1);
+		if (info->src.format == MDP_Y_CBCR_H2V1) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+		iowrite32((1  << 18) | 		/* chroma sampling 1=H2V1 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8,      		/* ROT_EN */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  2 << 19 | 		/* fetch planes 2 = pseudo */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  1 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9 |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int msm_rotator_ycxcx_h2v2(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned int in_chroma_paddr,
+				  unsigned int out_chroma_paddr)
+{
+	int bpp;
+	unsigned int in_chr_addr, out_chr_addr;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	if (!in_chroma_paddr) {
+		in_chr_addr = chroma_addr(in_paddr, info->src.width,
+				info->src.height,
+				bpp);
+	} else
+		in_chr_addr = in_chroma_paddr;
+
+	if (!out_chroma_paddr) {
+		out_chr_addr = chroma_addr(out_paddr, info->dst.width,
+				info->dst.height,
+				bpp);
+	} else
+		out_chr_addr = out_chroma_paddr;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_chr_addr,
+		  MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chr_addr +
+			((info->dst_y * info->dst.width)/2 + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width |
+			  info->src.width << 16,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+		iowrite32(info->dst.width |
+			  info->dst.width << 16,
+			  MSM_ROTATOR_OUT_YSTRIDE1);
+		if (info->src.format == MDP_Y_CBCR_H2V2) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+		iowrite32((3  << 18) | 		/* chroma sampling 3=4:2:0 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8,      		/* ROT_EN */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  2 << 19 | 		/* fetch planes 2 = pseudo */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  1 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9  |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+	return 0;
+}
+
+static unsigned int tile_size(unsigned int src_width,
+		unsigned int src_height,
+		const struct tile_parm *tp)
+{
+	unsigned int tile_w, tile_h;
+	unsigned int row_num_w, row_num_h;
+	tile_w = tp->width * tp->row_tile_w;
+	tile_h = tp->height * tp->row_tile_h;
+	row_num_w = (src_width + tile_w - 1) / tile_w;
+	row_num_h = (src_height + tile_h - 1) / tile_h;
+	return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
+}
+
+static int msm_rotator_ycxcx_h2v2_tile(struct msm_rotator_img_info *info,
+				  unsigned int in_paddr,
+				  unsigned int out_paddr,
+				  unsigned int use_imem,
+				  int new_session,
+				  unsigned in_chroma_paddr,
+				  unsigned out_chroma_paddr)
+{
+	int bpp;
+	unsigned int offset = 0;
+	unsigned int in_chr_addr, out_chr_addr;
+	/*
+	 * each row of samsung tile consists of two tiles in height
+	 * and two tiles in width which means width should align to
+	 * 64 x 2 bytes and height should align to 32 x 2 bytes.
+	 * video decoder generate two tiles in width and one tile
+	 * in height which ends up height align to 32 X 1 bytes.
+	 */
+	const struct tile_parm tile = {64, 32, 2, 1};
+	if ((info->src.format == MDP_Y_CRCB_H2V2_TILE &&
+		info->dst.format != MDP_Y_CRCB_H2V2) ||
+		(info->src.format == MDP_Y_CBCR_H2V2_TILE &&
+		info->dst.format != MDP_Y_CBCR_H2V2))
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	offset = tile_size(info->src.width, info->src.height, &tile);
+	if (!in_chroma_paddr)
+		in_chr_addr = in_paddr + offset;
+	else
+		in_chr_addr = in_chroma_paddr;
+
+	if (!out_chroma_paddr) {
+		out_chr_addr = chroma_addr(out_paddr, info->dst.width,
+				info->dst.height,
+				bpp);
+	} else
+		out_chr_addr = out_chroma_paddr;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(in_paddr + offset, MSM_ROTATOR_SRCP1_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+	iowrite32(out_chr_addr +
+			((info->dst_y * info->dst.width)/2 + info->dst_x),
+		  MSM_ROTATOR_OUTP1_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width |
+			  info->src.width << 16,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+
+		iowrite32(info->dst.width |
+			  info->dst.width << 16,
+			  MSM_ROTATOR_OUT_YSTRIDE1);
+		if (info->src.format == MDP_Y_CBCR_H2V2_TILE) {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		} else {
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		}
+		iowrite32((3  << 18) | 		/* chroma sampling 3=4:2:0 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8,      		/* ROT_EN */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(2 << 29 | 		/* frame format 2 = supertile */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  2 << 19 | 		/* fetch planes 2 = pseudo */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  1 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9  |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+	return 0;
+}
+
+static int msm_rotator_ycrycb(struct msm_rotator_img_info *info,
+			      unsigned int in_paddr,
+			      unsigned int out_paddr,
+			      unsigned int use_imem,
+			      int new_session)
+{
+	int bpp;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x),
+		  MSM_ROTATOR_OUTP0_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width,
+			  MSM_ROTATOR_SRC_YSTRIDE1);
+		iowrite32(info->dst.width,
+			  MSM_ROTATOR_OUT_YSTRIDE1);
+		iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
+			  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+		iowrite32(GET_PACK_PATTERN(CLR_Y, CLR_CR, CLR_Y, CLR_CB, 8),
+			  MSM_ROTATOR_OUT_PACK_PATTERN1);
+		iowrite32((1  << 18) | 		/* chroma sampling 1=H2V1 */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8,      		/* ROT_EN */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  0 << 19 | 		/* fetch planes 0=interleaved */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  3 << 13 | 		/* unpack count 0=1 component */
+			  (bpp-1) << 9 |	/* src Bpp 0=1 byte ... */
+			  0 << 8  | 		/* has alpha */
+			  0 << 6  | 		/* alpha bits 3=8bits */
+			  3 << 4  | 		/* R/Cr bits 1=5 2=6 3=8 */
+			  3 << 2  | 		/* B/Cb bits 1=5 2=6 3=8 */
+			  3 << 0,   		/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int msm_rotator_rgb_types(struct msm_rotator_img_info *info,
+				 unsigned int in_paddr,
+				 unsigned int out_paddr,
+				 unsigned int use_imem,
+				 int new_session)
+{
+	int bpp, abits, rbits, gbits, bbits;
+
+	if (info->src.format != info->dst.format)
+		return -EINVAL;
+
+	bpp = get_bpp(info->src.format);
+	if (bpp < 0)
+		return -ENOTTY;
+
+	iowrite32(in_paddr, MSM_ROTATOR_SRCP0_ADDR);
+	iowrite32(out_paddr +
+			((info->dst_y * info->dst.width) + info->dst_x) * bpp,
+		  MSM_ROTATOR_OUTP0_ADDR);
+
+	if (new_session) {
+		iowrite32(info->src.width * bpp, MSM_ROTATOR_SRC_YSTRIDE1);
+		iowrite32(info->dst.width * bpp, MSM_ROTATOR_OUT_YSTRIDE1);
+		iowrite32((0  << 18) | 		/* chroma sampling 0=rgb */
+			  (ROTATIONS_TO_BITMASK(info->rotations) << 9) |
+			  1 << 8,      		/* ROT_EN */
+			  MSM_ROTATOR_SUB_BLOCK_CFG);
+		switch (info->src.format) {
+		case MDP_RGB_565:
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_5BITS;
+			gbits = COMPONENT_6BITS;
+			bbits = COMPONENT_5BITS;
+			break;
+
+		case MDP_BGR_565:
+			iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_5BITS;
+			gbits = COMPONENT_6BITS;
+			bbits = COMPONENT_5BITS;
+			break;
+
+		case MDP_RGB_888:
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = 0;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		case MDP_ARGB_8888:
+		case MDP_RGBA_8888:
+		case MDP_XRGB_8888:
+		case MDP_RGBX_8888:
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
+						   CLR_B, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G,
+						   CLR_B, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = COMPONENT_8BITS;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		case MDP_BGRA_8888:
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
+						   CLR_R, 8),
+				  MSM_ROTATOR_SRC_UNPACK_PATTERN1);
+			iowrite32(GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G,
+						   CLR_R, 8),
+				  MSM_ROTATOR_OUT_PACK_PATTERN1);
+			abits = COMPONENT_8BITS;
+			rbits = COMPONENT_8BITS;
+			gbits = COMPONENT_8BITS;
+			bbits = COMPONENT_8BITS;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		iowrite32(0 << 29 | 		/* frame format 0 = linear */
+			  (use_imem ? 0 : 1) << 22 | /* tile size */
+			  0 << 19 | 		/* fetch planes 0=interleaved */
+			  0 << 18 | 		/* unpack align */
+			  1 << 17 | 		/* unpack tight */
+			  (abits ? 3 : 2) << 13 | /* unpack count 0=1 comp */
+			  (bpp-1) << 9 | 	/* src Bpp 0=1 byte ... */
+			  (abits ? 1 : 0) << 8  | /* has alpha */
+			  abits << 6  | 	/* alpha bits 3=8bits */
+			  rbits << 4  | 	/* R/Cr bits 1=5 2=6 3=8 */
+			  bbits << 2  | 	/* B/Cb bits 1=5 2=6 3=8 */
+			  gbits << 0,   	/* G/Y  bits 1=5 2=6 3=8 */
+			  MSM_ROTATOR_SRC_FORMAT);
+	}
+
+	return 0;
+}
+
+static int get_img(int memory_id, unsigned long *start, unsigned long *len,
+		struct file **pp_file)
+{
+	int ret = 0;
+#ifdef CONFIG_FB
+	struct file *file;
+	int put_needed, fb_num;
+#endif
+#ifdef CONFIG_ANDROID_PMEM
+	unsigned long vstart;
+#endif
+
+#ifdef CONFIG_ANDROID_PMEM
+	if (!get_pmem_file(memory_id, start, &vstart, len, pp_file))
+		return 0;
+#endif
+#ifdef CONFIG_FB
+	file = fget_light(memory_id, &put_needed);
+	if (file == NULL)
+		return -1;
+
+	if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+		fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+		if (get_fb_phys_info(start, len, fb_num))
+			ret = -1;
+		else
+			*pp_file = file;
+	} else
+		ret = -1;
+	if (ret)
+		fput_light(file, put_needed);
+#endif
+	return ret;
+}
+
+static int msm_rotator_do_rotate(unsigned long arg)
+{
+	int rc = 0;
+	unsigned int status;
+	struct msm_rotator_data_info info;
+	unsigned int in_paddr, out_paddr;
+	unsigned long len;
+	struct file *src_file = 0;
+	struct file *dst_file = 0;
+	int use_imem = 0;
+	int s;
+	struct file *src_chroma_file = 0;
+	struct file *dst_chroma_file = 0;
+	unsigned int in_chroma_paddr = 0, out_chroma_paddr = 0;
+	uint32_t format;
+
+	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+		return -EFAULT;
+
+	rc = get_img(info.src.memory_id, (unsigned long *)&in_paddr,
+			(unsigned long *)&len, &src_file);
+	if (rc) {
+		printk(KERN_ERR "%s: in get_img() failed id=0x%08x\n",
+		       DRIVER_NAME, info.src.memory_id);
+		return rc;
+	}
+	in_paddr += info.src.offset;
+
+	rc = get_img(info.dst.memory_id, (unsigned long *)&out_paddr,
+			(unsigned long *)&len, &dst_file);
+	if (rc) {
+		printk(KERN_ERR "%s: out get_img() failed id=0x%08x\n",
+		       DRIVER_NAME, info.dst.memory_id);
+		goto do_rotate_fail_dst_img;
+	}
+	out_paddr += info.dst.offset;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++)
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(info.session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s]
+			))
+			break;
+
+	if (s == MAX_SESSIONS) {
+		dev_dbg(msm_rotator_dev->device,
+			"%s() : Attempt to use invalid session_id %d\n",
+			__func__, s);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+
+	if (msm_rotator_dev->img_info[s]->enable == 0) {
+		dev_dbg(msm_rotator_dev->device,
+			"%s() : Session_id %d not enabled \n",
+			__func__, s);
+		rc = -EINVAL;
+		goto do_rotate_unlock_mutex;
+	}
+
+	format = msm_rotator_dev->img_info[s]->src.format;
+	if (((info.version_key & VERSION_KEY_MASK) == 0xA5B4C300) &&
+		((info.version_key & ~VERSION_KEY_MASK) > 0) &&
+		 (format == MDP_Y_CBCR_H2V2 ||
+		  format == MDP_Y_CRCB_H2V2 ||
+		  format == MDP_Y_CRCB_H2V2_TILE ||
+		  format == MDP_Y_CBCR_H2V2_TILE ||
+		  format == MDP_Y_CBCR_H2V1 ||
+		  format == MDP_Y_CRCB_H2V1)) {
+		rc = get_img(info.src_chroma.memory_id,
+				(unsigned long *)&in_chroma_paddr,
+				(unsigned long *)&len, &src_chroma_file);
+		if (rc) {
+			printk(KERN_ERR "%s: in chroma get_img() failed id=0x%08x\n",
+				DRIVER_NAME, info.src_chroma.memory_id);
+			goto do_rotate_unlock_mutex;
+		}
+		in_chroma_paddr += info.src_chroma.offset;
+
+		rc = get_img(info.dst_chroma.memory_id,
+				(unsigned long *)&out_chroma_paddr,
+				(unsigned long *)&len, &dst_chroma_file);
+		if (rc) {
+			printk(KERN_ERR "%s: out chroma get_img() failed id=0x%08x\n",
+				DRIVER_NAME, info.dst_chroma.memory_id);
+			goto do_rotate_fail_dst_chr_img;
+		}
+		out_chroma_paddr += info.dst_chroma.offset;
+	}
+
+	cancel_delayed_work(&msm_rotator_dev->rot_clk_work);
+	if (msm_rotator_dev->rot_clk_state != CLK_EN) {
+		enable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_EN;
+	}
+	enable_irq(msm_rotator_dev->irq);
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	use_imem = msm_rotator_imem_allocate(ROTATOR_REQUEST);
+#else
+	use_imem = 0;
+#endif
+	/*
+	 * workaround for a hardware bug. rotator hardware hangs when we
+	 * use write burst beat size 16 on 128X128 tile fetch mode. As a
+	 * temporary fix use 0x42 for BURST_SIZE when imem used.
+	 */
+	if (use_imem)
+		iowrite32(0x42, MSM_ROTATOR_MAX_BURST_SIZE);
+
+	iowrite32(((msm_rotator_dev->img_info[s]->src_rect.h & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src_rect.w & 0x1fff),
+		  MSM_ROTATOR_SRC_SIZE);
+	iowrite32(((msm_rotator_dev->img_info[s]->src_rect.y & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src_rect.x & 0x1fff),
+		  MSM_ROTATOR_SRC_XY);
+	iowrite32(((msm_rotator_dev->img_info[s]->src.height & 0x1fff)
+				<< 16) |
+		  (msm_rotator_dev->img_info[s]->src.width & 0x1fff),
+		  MSM_ROTATOR_SRC_IMAGE_SIZE);
+
+	switch (format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_RGB_888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_XRGB_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
+		rc = msm_rotator_rgb_types(msm_rotator_dev->img_info[s],
+					   in_paddr, out_paddr,
+					   use_imem,
+					   msm_rotator_dev->last_session_idx
+								!= s);
+		break;
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+		rc = msm_rotator_ycxcx_h2v2(msm_rotator_dev->img_info[s],
+					    in_paddr, out_paddr, use_imem,
+					    msm_rotator_dev->last_session_idx
+								!= s,
+					    in_chroma_paddr,
+					    out_chroma_paddr);
+		break;
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		rc = msm_rotator_ycxcx_h2v2_tile(msm_rotator_dev->img_info[s],
+				in_paddr, out_paddr, use_imem,
+				msm_rotator_dev->last_session_idx
+				!= s,
+				in_chroma_paddr,
+				out_chroma_paddr);
+	break;
+
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+		rc = msm_rotator_ycxcx_h2v1(msm_rotator_dev->img_info[s],
+					    in_paddr, out_paddr, use_imem,
+					    msm_rotator_dev->last_session_idx
+								!= s,
+					    in_chroma_paddr,
+					    out_chroma_paddr);
+		break;
+	case MDP_YCRYCB_H2V1:
+		rc = msm_rotator_ycrycb(msm_rotator_dev->img_info[s],
+				in_paddr, out_paddr, use_imem,
+				msm_rotator_dev->last_session_idx != s);
+		break;
+	default:
+		rc = -EINVAL;
+		goto do_rotate_exit;
+	}
+
+	if (rc != 0) {
+		msm_rotator_dev->last_session_idx = INVALID_SESSION;
+		goto do_rotate_exit;
+	}
+
+	iowrite32(3, MSM_ROTATOR_INTR_ENABLE);
+
+	msm_rotator_dev->processing = 1;
+	iowrite32(0x1, MSM_ROTATOR_START);
+
+	wait_event(msm_rotator_dev->wq,
+		   (msm_rotator_dev->processing == 0));
+	status = (unsigned char)ioread32(MSM_ROTATOR_INTR_STATUS);
+	if ((status & 0x03) != 0x01)
+		rc = -EFAULT;
+	iowrite32(0, MSM_ROTATOR_INTR_ENABLE);
+	iowrite32(3, MSM_ROTATOR_INTR_CLEAR);
+
+do_rotate_exit:
+	disable_irq(msm_rotator_dev->irq);
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	msm_rotator_imem_free(ROTATOR_REQUEST);
+#endif
+	schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
+	if (dst_chroma_file)
+		put_pmem_file(dst_chroma_file);
+do_rotate_fail_dst_chr_img:
+	if (src_chroma_file)
+		put_pmem_file(src_chroma_file);
+do_rotate_unlock_mutex:
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	if (dst_file)
+		put_pmem_file(dst_file);
+do_rotate_fail_dst_img:
+	if (src_file)
+		put_pmem_file(src_file);
+	dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
+		__func__, rc);
+	return rc;
+}
+
+static int msm_rotator_start(unsigned long arg, int pid)
+{
+	struct msm_rotator_img_info info;
+	int rc = 0;
+	int s;
+	int first_free_index = INVALID_SESSION;
+
+	if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+		return -EFAULT;
+
+	if ((info.rotations > MSM_ROTATOR_MAX_ROT) ||
+	    (info.src.height > MSM_ROTATOR_MAX_H) ||
+	    (info.src.width > MSM_ROTATOR_MAX_W) ||
+	    (info.dst.height > MSM_ROTATOR_MAX_H) ||
+	    (info.dst.width > MSM_ROTATOR_MAX_W) ||
+	    ((info.src_rect.x + info.src_rect.w) > info.src.width) ||
+	    ((info.src_rect.y + info.src_rect.h) > info.src.height) ||
+	    ((info.rotations & MDP_ROT_90) &&
+		((info.dst_x + info.src_rect.h) > info.dst.width)) ||
+	    ((info.rotations & MDP_ROT_90) &&
+		((info.dst_y + info.src_rect.w) > info.dst.height)) ||
+	    (!(info.rotations & MDP_ROT_90) &&
+		((info.dst_x + info.src_rect.w) > info.dst.width)) ||
+	    (!(info.rotations & MDP_ROT_90) &&
+		((info.dst_y + info.src_rect.h) > info.dst.height)))
+		return -EINVAL;
+
+	switch (info.src.format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_RGB_888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_XRGB_8888:
+	case MDP_RGBX_8888:
+	case MDP_BGRA_8888:
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_YCRYCB_H2V1:
+	case MDP_Y_CRCB_H2V2_TILE:
+	case MDP_Y_CBCR_H2V2_TILE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (info.dst.format) {
+	case MDP_RGB_565:
+	case MDP_BGR_565:
+	case MDP_RGB_888:
+	case MDP_ARGB_8888:
+	case MDP_RGBA_8888:
+	case MDP_XRGB_8888:
+	case MDP_RGBX_8888:
+	case MDP_BGRA_8888:
+	case MDP_Y_CBCR_H2V2:
+	case MDP_Y_CRCB_H2V2:
+	case MDP_Y_CBCR_H2V1:
+	case MDP_Y_CRCB_H2V1:
+	case MDP_YCRYCB_H2V1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(info.session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s]
+			)) {
+			*(msm_rotator_dev->img_info[s]) = info;
+			msm_rotator_dev->pid_list[s] = pid;
+
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+				INVALID_SESSION;
+			break;
+		}
+
+		if ((msm_rotator_dev->img_info[s] == NULL) &&
+			(first_free_index ==
+			INVALID_SESSION))
+			first_free_index = s;
+	}
+
+	if ((s == MAX_SESSIONS) && (first_free_index != INVALID_SESSION)) {
+		/* allocate a session id */
+		msm_rotator_dev->img_info[first_free_index] =
+			kzalloc(sizeof(struct msm_rotator_img_info),
+					GFP_KERNEL);
+		if (!msm_rotator_dev->img_info[first_free_index]) {
+			printk(KERN_ERR "%s : unable to alloc mem\n",
+					__func__);
+			rc = -ENOMEM;
+			goto rotator_start_exit;
+		}
+		info.session_id = (unsigned int)
+			msm_rotator_dev->img_info[first_free_index];
+		*(msm_rotator_dev->img_info[first_free_index]) = info;
+		msm_rotator_dev->pid_list[first_free_index] = pid;
+
+		if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+			rc = -EFAULT;
+	} else if (s == MAX_SESSIONS) {
+		dev_dbg(msm_rotator_dev->device, "%s: all sessions in use\n",
+			__func__);
+		rc = -EBUSY;
+	}
+
+rotator_start_exit:
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	return rc;
+}
+
+static int msm_rotator_finish(unsigned long arg)
+{
+	int rc = 0;
+	int s;
+	unsigned int session_id;
+
+	if (copy_from_user(&session_id, (void __user *)arg, sizeof(s)))
+		return -EFAULT;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if ((msm_rotator_dev->img_info[s] != NULL) &&
+			(session_id ==
+			(unsigned int)msm_rotator_dev->img_info[s])) {
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+					INVALID_SESSION;
+			kfree(msm_rotator_dev->img_info[s]);
+			msm_rotator_dev->img_info[s] = NULL;
+			msm_rotator_dev->pid_list[s] = 0;
+			break;
+		}
+	}
+
+	if (s == MAX_SESSIONS)
+		rc = -EINVAL;
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return rc;
+}
+
+static int
+msm_rotator_open(struct inode *inode, struct file *filp)
+{
+	int *id;
+	int i;
+
+	if (filp->private_data)
+		return -EBUSY;
+
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	id = &msm_rotator_dev->pid_list[0];
+	for (i = 0; i < MAX_SESSIONS; i++, id++) {
+		if (*id == 0)
+			break;
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	if (i == MAX_SESSIONS)
+		return -EBUSY;
+
+	filp->private_data = (void *)task_tgid_nr(current);
+
+	return 0;
+}
+
+static int
+msm_rotator_close(struct inode *inode, struct file *filp)
+{
+	int s;
+	int pid;
+
+	pid = (int)filp->private_data;
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	for (s = 0; s < MAX_SESSIONS; s++) {
+		if (msm_rotator_dev->img_info[s] != NULL &&
+			msm_rotator_dev->pid_list[s] == pid) {
+			kfree(msm_rotator_dev->img_info[s]);
+			msm_rotator_dev->img_info[s] = NULL;
+			if (msm_rotator_dev->last_session_idx == s)
+				msm_rotator_dev->last_session_idx =
+					INVALID_SESSION;
+		}
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+
+	return 0;
+}
+
+static long msm_rotator_ioctl(struct file *file, unsigned cmd,
+						 unsigned long arg)
+{
+	int pid;
+
+	if (_IOC_TYPE(cmd) != MSM_ROTATOR_IOCTL_MAGIC)
+		return -ENOTTY;
+
+	pid = (int)file->private_data;
+
+	switch (cmd) {
+	case MSM_ROTATOR_IOCTL_START:
+		return msm_rotator_start(arg, pid);
+	case MSM_ROTATOR_IOCTL_ROTATE:
+		return msm_rotator_do_rotate(arg);
+	case MSM_ROTATOR_IOCTL_FINISH:
+		return msm_rotator_finish(arg);
+
+	default:
+		dev_dbg(msm_rotator_dev->device,
+			"unexpected IOCTL %d\n", cmd);
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations msm_rotator_fops = {
+	.owner = THIS_MODULE,
+	.open = msm_rotator_open,
+	.release = msm_rotator_close,
+	.unlocked_ioctl = msm_rotator_ioctl,
+};
+
+static int __devinit msm_rotator_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *res;
+	struct msm_rotator_platform_data *pdata = NULL;
+	int i, number_of_clks;
+	uint32_t ver;
+
+	msm_rotator_dev = kzalloc(sizeof(struct msm_rotator_dev), GFP_KERNEL);
+	if (!msm_rotator_dev) {
+		printk(KERN_ERR "%s Unable to allocate memory for struct\n",
+		       __func__);
+		return -ENOMEM;
+	}
+	for (i = 0; i < MAX_SESSIONS; i++)
+		msm_rotator_dev->img_info[i] = NULL;
+	msm_rotator_dev->last_session_idx = INVALID_SESSION;
+
+	pdata = pdev->dev.platform_data;
+	number_of_clks = pdata->number_of_clocks;
+
+	msm_rotator_dev->imem_owner = IMEM_NO_OWNER;
+	mutex_init(&msm_rotator_dev->imem_lock);
+	msm_rotator_dev->imem_clk_state = CLK_DIS;
+	INIT_DELAYED_WORK(&msm_rotator_dev->imem_clk_work,
+			  msm_rotator_imem_clk_work_f);
+	msm_rotator_dev->imem_clk = NULL;
+	msm_rotator_dev->pdev = pdev;
+
+	msm_rotator_dev->core_clk = NULL;
+	msm_rotator_dev->pclk = NULL;
+	msm_rotator_dev->axi_clk = NULL;
+
+	for (i = 0; i < number_of_clks; i++) {
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_IMEM_CLK) {
+			msm_rotator_dev->imem_clk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->imem_clk)) {
+				rc = PTR_ERR(msm_rotator_dev->imem_clk);
+				msm_rotator_dev->imem_clk = NULL;
+				printk(KERN_ERR "%s: cannot get imem_clk "
+					"rc=%d\n", DRIVER_NAME, rc);
+				goto error_imem_clk;
+			}
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_min_rate(msm_rotator_dev->imem_clk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_PCLK) {
+			msm_rotator_dev->pclk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->pclk)) {
+				rc = PTR_ERR(msm_rotator_dev->pclk);
+				msm_rotator_dev->pclk = NULL;
+				printk(KERN_ERR "%s: cannot get pclk rc=%d\n",
+					DRIVER_NAME, rc);
+				goto error_pclk;
+			}
+
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_min_rate(msm_rotator_dev->pclk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_CORE_CLK) {
+			msm_rotator_dev->core_clk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->core_clk)) {
+				rc = PTR_ERR(msm_rotator_dev->core_clk);
+				msm_rotator_dev->core_clk = NULL;
+				printk(KERN_ERR "%s: cannot get core clk "
+					"rc=%d\n", DRIVER_NAME, rc);
+			goto error_core_clk;
+			}
+
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_min_rate(msm_rotator_dev->core_clk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+
+		if (pdata->rotator_clks[i].clk_type == ROTATOR_AXI_CLK) {
+			msm_rotator_dev->axi_clk =
+			clk_get(&msm_rotator_dev->pdev->dev,
+				pdata->rotator_clks[i].clk_name);
+			if (IS_ERR(msm_rotator_dev->axi_clk)) {
+				rc = PTR_ERR(msm_rotator_dev->axi_clk);
+				msm_rotator_dev->axi_clk = NULL;
+				printk(KERN_ERR "%s: cannot get axi clk "
+					"rc=%d\n", DRIVER_NAME, rc);
+			goto error_axi_clk;
+			}
+
+			if (pdata->rotator_clks[i].clk_rate)
+				clk_set_min_rate(msm_rotator_dev->axi_clk,
+					pdata->rotator_clks[i].clk_rate);
+		}
+	}
+
+	msm_rotator_dev->regulator = regulator_get(NULL, pdata->regulator_name);
+	if (IS_ERR(msm_rotator_dev->regulator))
+		msm_rotator_dev->regulator = NULL;
+
+	msm_rotator_dev->rot_clk_state = CLK_DIS;
+	INIT_DELAYED_WORK(&msm_rotator_dev->rot_clk_work,
+			  msm_rotator_rot_clk_work_f);
+
+	mutex_init(&msm_rotator_dev->rotator_lock);
+
+	platform_set_drvdata(pdev, msm_rotator_dev);
+
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		printk(KERN_ALERT
+		       "%s: could not get IORESOURCE_MEM\n", DRIVER_NAME);
+		rc = -ENODEV;
+		goto error_get_resource;
+	}
+	msm_rotator_dev->io_base = ioremap(res->start,
+					   resource_size(res));
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_clk)
+		clk_enable(msm_rotator_dev->imem_clk);
+#endif
+	enable_rot_clks();
+	ver = ioread32(MSM_ROTATOR_HW_VERSION);
+	disable_rot_clks();
+
+#ifdef CONFIG_MSM_ROTATOR_USE_IMEM
+	if (msm_rotator_dev->imem_clk)
+		clk_disable(msm_rotator_dev->imem_clk);
+#endif
+	if (ver != pdata->hardware_version_number) {
+		printk(KERN_ALERT "%s: invalid HW version\n", DRIVER_NAME);
+		rc = -ENODEV;
+		goto error_get_resource;
+	}
+	msm_rotator_dev->irq = platform_get_irq(pdev, 0);
+	if (msm_rotator_dev->irq < 0) {
+		printk(KERN_ALERT "%s: could not get IORESOURCE_IRQ\n",
+		       DRIVER_NAME);
+		rc = -ENODEV;
+		goto error_get_irq;
+	}
+	rc = request_irq(msm_rotator_dev->irq, msm_rotator_isr,
+			 IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
+	if (rc) {
+		printk(KERN_ERR "%s: request_irq() failed\n", DRIVER_NAME);
+		goto error_get_irq;
+	}
+	/* we enable the IRQ when we need it in the ioctl */
+	disable_irq(msm_rotator_dev->irq);
+
+	rc = alloc_chrdev_region(&msm_rotator_dev->dev_num, 0, 1, DRIVER_NAME);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: alloc_chrdev_region Failed rc = %d\n",
+		       __func__, rc);
+		goto error_get_irq;
+	}
+
+	msm_rotator_dev->class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(msm_rotator_dev->class)) {
+		rc = PTR_ERR(msm_rotator_dev->class);
+		printk(KERN_ERR "%s: couldn't create class rc = %d\n",
+		       DRIVER_NAME, rc);
+		goto error_class_create;
+	}
+
+	msm_rotator_dev->device = device_create(msm_rotator_dev->class, NULL,
+						msm_rotator_dev->dev_num, NULL,
+						DRIVER_NAME);
+	if (IS_ERR(msm_rotator_dev->device)) {
+		rc = PTR_ERR(msm_rotator_dev->device);
+		printk(KERN_ERR "%s: device_create failed %d\n",
+		       DRIVER_NAME, rc);
+		goto error_class_device_create;
+	}
+
+	cdev_init(&msm_rotator_dev->cdev, &msm_rotator_fops);
+	rc = cdev_add(&msm_rotator_dev->cdev,
+		      MKDEV(MAJOR(msm_rotator_dev->dev_num), 0),
+		      1);
+	if (rc < 0) {
+		printk(KERN_ERR "%s: cdev_add failed %d\n", __func__, rc);
+		goto error_cdev_add;
+	}
+
+	init_waitqueue_head(&msm_rotator_dev->wq);
+
+	dev_dbg(msm_rotator_dev->device, "probe successful\n");
+	return rc;
+
+error_cdev_add:
+	device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
+error_class_device_create:
+	class_destroy(msm_rotator_dev->class);
+error_class_create:
+	unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
+error_get_irq:
+	iounmap(msm_rotator_dev->io_base);
+error_get_resource:
+	mutex_destroy(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->regulator)
+		regulator_put(msm_rotator_dev->regulator);
+	clk_put(msm_rotator_dev->axi_clk);
+error_axi_clk:
+	clk_put(msm_rotator_dev->core_clk);
+error_core_clk:
+	clk_put(msm_rotator_dev->pclk);
+error_pclk:
+	if (msm_rotator_dev->imem_clk)
+		clk_put(msm_rotator_dev->imem_clk);
+error_imem_clk:
+	mutex_destroy(&msm_rotator_dev->imem_lock);
+	kfree(msm_rotator_dev);
+	return rc;
+}
+
+static int __devexit msm_rotator_remove(struct platform_device *plat_dev)
+{
+	int i;
+
+	free_irq(msm_rotator_dev->irq, NULL);
+	mutex_destroy(&msm_rotator_dev->rotator_lock);
+	cdev_del(&msm_rotator_dev->cdev);
+	device_destroy(msm_rotator_dev->class, msm_rotator_dev->dev_num);
+	class_destroy(msm_rotator_dev->class);
+	unregister_chrdev_region(msm_rotator_dev->dev_num, 1);
+	iounmap(msm_rotator_dev->io_base);
+	if (msm_rotator_dev->imem_clk) {
+		if (msm_rotator_dev->imem_clk_state == CLK_EN)
+			clk_disable(msm_rotator_dev->imem_clk);
+		clk_put(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk = NULL;
+	}
+	if (msm_rotator_dev->rot_clk_state == CLK_EN)
+		disable_rot_clks();
+	clk_put(msm_rotator_dev->core_clk);
+	clk_put(msm_rotator_dev->pclk);
+	clk_put(msm_rotator_dev->axi_clk);
+	if (msm_rotator_dev->regulator)
+		regulator_put(msm_rotator_dev->regulator);
+	msm_rotator_dev->core_clk = NULL;
+	msm_rotator_dev->pclk = NULL;
+	msm_rotator_dev->axi_clk = NULL;
+	mutex_destroy(&msm_rotator_dev->imem_lock);
+	for (i = 0; i < MAX_SESSIONS; i++)
+		if (msm_rotator_dev->img_info[i] != NULL)
+			kfree(msm_rotator_dev->img_info[i]);
+	kfree(msm_rotator_dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_rotator_suspend(struct platform_device *dev, pm_message_t state)
+{
+	mutex_lock(&msm_rotator_dev->imem_lock);
+	if (msm_rotator_dev->imem_clk_state == CLK_EN
+		&& msm_rotator_dev->imem_clk) {
+		clk_disable(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk_state = CLK_SUSPEND;
+	}
+	mutex_unlock(&msm_rotator_dev->imem_lock);
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->rot_clk_state == CLK_EN) {
+		disable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_SUSPEND;
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return 0;
+}
+
+static int msm_rotator_resume(struct platform_device *dev)
+{
+	mutex_lock(&msm_rotator_dev->imem_lock);
+	if (msm_rotator_dev->imem_clk_state == CLK_SUSPEND
+		&& msm_rotator_dev->imem_clk) {
+		clk_enable(msm_rotator_dev->imem_clk);
+		msm_rotator_dev->imem_clk_state = CLK_EN;
+	}
+	mutex_unlock(&msm_rotator_dev->imem_lock);
+	mutex_lock(&msm_rotator_dev->rotator_lock);
+	if (msm_rotator_dev->rot_clk_state == CLK_SUSPEND) {
+		enable_rot_clks();
+		msm_rotator_dev->rot_clk_state = CLK_EN;
+	}
+	mutex_unlock(&msm_rotator_dev->rotator_lock);
+	return 0;
+}
+#endif
+
+static struct platform_driver msm_rotator_platform_driver = {
+	.probe = msm_rotator_probe,
+	.remove = __devexit_p(msm_rotator_remove),
+#ifdef CONFIG_PM
+	.suspend = msm_rotator_suspend,
+	.resume = msm_rotator_resume,
+#endif
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME
+	}
+};
+
+static int __init msm_rotator_init(void)
+{
+	return platform_driver_register(&msm_rotator_platform_driver);
+}
+
+static void __exit msm_rotator_exit(void)
+{
+	return platform_driver_unregister(&msm_rotator_platform_driver);
+}
+
+module_init(msm_rotator_init);
+module_exit(msm_rotator_exit);
+
+MODULE_DESCRIPTION("MSM Offline Image Rotator driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
index b6f8a65..a8e28d3 100644
--- a/drivers/char/msm_smd_pkt.c
+++ b/drivers/char/msm_smd_pkt.c
@@ -9,11 +9,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
  */
 /*
  * SMD Packet Driver -- Provides userspace interface to SMD packet ports.
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index f6595ab..c720e9a 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -60,4 +60,18 @@
 	  Further information on this driver and the supported hardware
 	  can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ 
 
+config TCG_ST_I2C
+	tristate "ST Micro ST19NP18-TPM-I2C TPM interface"
+	depends on I2C
+	default n
+	---help---
+	  If you have a ST19NP18-TPM-I2C TPM security chip from ST Micro
+	  say Yes and it will be accessible from Linux.
+
+config TCG_TPMD_DEV
+	tristate "tpmd_dev TPM Emulator driver"
+	default n
+	---help---
+	  Enables the TPM emulator driver
+
 endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index ea3a1e0..c113cf1 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -6,6 +6,8 @@
 	obj-$(CONFIG_TCG_TPM) += tpm_bios.o
 endif
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_ST_I2C) += tpm_st_i2c.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
+obj-$(CONFIG_TCG_TPMD_DEV) += tpmd_dev/
diff --git a/drivers/char/tpm/tpm_st_i2c.c b/drivers/char/tpm/tpm_st_i2c.c
new file mode 100644
index 0000000..3a6e8c4f
--- /dev/null
+++ b/drivers/char/tpm/tpm_st_i2c.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2010, 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/i2c.h>
+#include <linux/irq.h>
+#include <mach/gpio.h>
+#include <mach/tpm_st_i2c.h>
+#include <mach/msm_iomap.h>
+#include "tpm.h"
+
+#define DEVICE_NAME "tpm_st_i2c"
+
+#define TPM_HEADER_LEN sizeof(struct tpm_input_header)
+#define TPM_ST_I2C_BLOCK_MAX 40
+
+struct tpm_st_i2c_dev {
+	struct i2c_client *client;
+	struct tpm_st_i2c_platform_data *pd;
+	struct completion com[2];
+};
+
+/* for completion array */
+#define ACCEPT_CMD_INDEX 0
+#define DATA_AVAIL_INDEX 1
+
+static struct tpm_st_i2c_dev *tpm_st_i2c_dev;
+
+#define TPM_ST_I2C_REQ_COMPLETE_MASK 1
+
+static u8 tpm_st_i2c_status(struct tpm_chip *chip)
+{
+	int gpio = tpm_st_i2c_dev->pd->data_avail_gpio;
+	return gpio_get_value(gpio);
+}
+
+static void tpm_st_i2c_cancel(struct tpm_chip *chip)
+{
+	/* not supported */
+	return;
+}
+
+static int tpm_st_i2c_transfer_buf(struct tpm_chip *chip, u8 *buf, size_t count,
+				   int recv)
+{
+	struct i2c_msg msg = {
+		.addr = tpm_st_i2c_dev->client->addr,
+		.flags = 0,
+		.buf = buf,
+		.len = TPM_HEADER_LEN, /* must read/write header first */
+	};
+	int gpio;
+	int irq;
+	struct completion *com;
+	__be32 *native_size;
+	int read_header = 0;
+	int rc = 0;
+	int len = count;
+	uint32_t size = count;
+	int tmp;
+
+	if (recv) {
+		msg.flags |= I2C_M_RD;
+		read_header = 1;
+		gpio = tpm_st_i2c_dev->pd->data_avail_gpio;
+		irq = tpm_st_i2c_dev->pd->data_avail_irq;
+		com = &tpm_st_i2c_dev->com[DATA_AVAIL_INDEX];
+	} else {
+		gpio = tpm_st_i2c_dev->pd->accept_cmd_gpio;
+		irq = tpm_st_i2c_dev->pd->accept_cmd_irq;
+		com = &tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX];
+	}
+
+	if (len < TPM_HEADER_LEN) {
+		dev_dbg(chip->dev, "%s: invalid len\n", __func__);
+		return -EINVAL;
+	}
+
+	do {
+		if (!gpio_get_value(gpio)) {
+			/* reset the completion in case the irq fired
+			 * during the probe
+			 */
+			init_completion(com);
+			enable_irq(irq);
+			tmp = wait_for_completion_interruptible_timeout(
+				com, HZ/2);
+			if (!tmp) {
+				dev_dbg(chip->dev, "%s timeout\n",
+					__func__);
+				return -EBUSY;
+			}
+		}
+		rc = i2c_transfer(tpm_st_i2c_dev->client->adapter,
+				  &msg, 1);
+		if (rc < 0) {
+			dev_dbg(chip->dev, "Error in I2C transfer\n");
+			return rc;
+		}
+		if (read_header) {
+			read_header = 0;
+			native_size = (__force __be32 *) (buf + 2);
+			size = be32_to_cpu(*native_size);
+			if (count < size) {
+				dev_dbg(chip->dev,
+					"%s: invalid count\n",
+					__func__);
+				rc = -EIO;
+			}
+			len = size;
+		}
+		len -= msg.len;
+		if (len) {
+			buf += msg.len;
+			msg.buf = buf;
+			if (len > TPM_ST_I2C_BLOCK_MAX)
+				msg.len = TPM_ST_I2C_BLOCK_MAX;
+			else
+				msg.len = len;
+		}
+	} while (len > 0);
+
+	if (rc >= 0)
+		return size;
+	else
+		return rc;
+}
+
+static int tpm_st_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+	return tpm_st_i2c_transfer_buf(chip, buf, count, 1);
+}
+
+static int tpm_st_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+	return tpm_st_i2c_transfer_buf(chip, buf, len, 0);
+}
+
+#ifdef CONFIG_PM
+static int tpm_st_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return tpm_pm_suspend(&client->dev, msg);
+}
+
+static int tpm_st_i2c_resume(struct i2c_client *client)
+{
+	return tpm_pm_resume(&client->dev);
+}
+#endif
+
+static const struct file_operations tpm_st_i2c_fs_ops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.open = tpm_open,
+	.read = tpm_read,
+	.write = tpm_write,
+	.release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
+		   NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+
+static struct attribute *tpm_st_i2c_attrs[] = {
+	&dev_attr_pubek.attr,
+	&dev_attr_pcrs.attr,
+	&dev_attr_enabled.attr,
+	&dev_attr_active.attr,
+	&dev_attr_owned.attr,
+	&dev_attr_temp_deactivated.attr,
+	&dev_attr_caps.attr,
+	NULL,
+};
+
+static struct attribute_group tpm_st_i2c_attr_grp = {
+	.attrs = tpm_st_i2c_attrs
+};
+
+static struct tpm_vendor_specific tpm_st_i2c_vendor = {
+	.status = tpm_st_i2c_status,
+	.recv = tpm_st_i2c_recv,
+	.send = tpm_st_i2c_send,
+	.cancel = tpm_st_i2c_cancel,
+	.req_complete_mask = TPM_ST_I2C_REQ_COMPLETE_MASK,
+	.req_complete_val = TPM_ST_I2C_REQ_COMPLETE_MASK,
+	.req_canceled = 0xff,  /* not supported */
+	.attr_group = &tpm_st_i2c_attr_grp,
+	.miscdev = {
+		    .fops = &tpm_st_i2c_fs_ops,},
+};
+
+static irqreturn_t tpm_st_i2c_isr(int irq, void *dev_id)
+{
+	disable_irq_nosync(irq);
+	if (irq == tpm_st_i2c_dev->pd->accept_cmd_irq)
+		complete(&tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX]);
+	else
+		complete(&tpm_st_i2c_dev->com[DATA_AVAIL_INDEX]);
+	return IRQ_HANDLED;
+}
+
+static int tpm_st_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct tpm_st_i2c_platform_data *pd;
+	struct  tpm_chip *chip;
+	int high;
+
+	dev_dbg(&client->dev, "%s()\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE |
+				     I2C_FUNC_SMBUS_I2C_BLOCK |
+				     I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "incompatible adapter\n");
+		return -ENODEV;
+	}
+
+	pd = client->dev.platform_data;
+	if (!pd || !pd->gpio_setup || !pd->gpio_release) {
+		dev_err(&client->dev, "platform data not setup\n");
+		rc = -EFAULT;
+		goto no_platform_data;
+	}
+	rc = pd->gpio_setup();
+	if (rc) {
+		dev_err(&client->dev, "gpio_setup failed\n");
+		goto gpio_setup_fail;
+	}
+
+	gpio_direction_input(pd->accept_cmd_gpio);
+	gpio_direction_input(pd->data_avail_gpio);
+
+	tpm_st_i2c_dev = kzalloc(sizeof(struct tpm_st_i2c_dev), GFP_KERNEL);
+	if (!tpm_st_i2c_dev) {
+		printk(KERN_ERR "%s Unable to allocate memory for struct\n",
+		       __func__);
+		rc = -ENOMEM;
+		goto kzalloc_fail;
+	}
+
+	tpm_st_i2c_dev->client = client;
+	tpm_st_i2c_dev->pd = pd;
+
+	init_completion(&tpm_st_i2c_dev->com[ACCEPT_CMD_INDEX]);
+	init_completion(&tpm_st_i2c_dev->com[DATA_AVAIL_INDEX]);
+	/* This logic allows us to setup irq but not have it enabled, in
+	 * case the lines are already active
+	 */
+	high = gpio_get_value(pd->data_avail_gpio);
+	rc = request_irq(pd->data_avail_irq, tpm_st_i2c_isr, IRQF_TRIGGER_HIGH,
+			 DEVICE_NAME "-data", NULL);
+	if (rc) {
+		dev_err(&client->dev, "request for data irq failed\n");
+		goto data_irq_fail;
+	}
+	if (!high)
+		disable_irq(pd->data_avail_irq);
+	high = gpio_get_value(pd->accept_cmd_gpio);
+	rc = request_irq(pd->accept_cmd_irq, tpm_st_i2c_isr, IRQF_TRIGGER_HIGH,
+			 DEVICE_NAME "-cmd", NULL);
+	if (rc) {
+		dev_err(&client->dev, "request for cmd irq failed\n");
+		goto cmd_irq_fail;
+	}
+	if (!high)
+		disable_irq(pd->accept_cmd_irq);
+
+	tpm_st_i2c_vendor.irq = pd->data_avail_irq;
+
+	chip = tpm_register_hardware(&client->dev, &tpm_st_i2c_vendor);
+	if (!chip) {
+		dev_err(&client->dev, "Could not register tpm hardware\n");
+		rc = -ENODEV;
+		goto tpm_reg_fail;
+	}
+
+	dev_info(&client->dev, "added\n");
+
+	return 0;
+
+tpm_reg_fail:
+	free_irq(pd->accept_cmd_irq, NULL);
+cmd_irq_fail:
+	free_irq(pd->data_avail_irq, NULL);
+data_irq_fail:
+kzalloc_fail:
+	pd->gpio_release();
+gpio_setup_fail:
+no_platform_data:
+
+	return rc;
+}
+
+static int __exit tpm_st_i2c_remove(struct i2c_client *client)
+{
+	free_irq(tpm_st_i2c_dev->pd->accept_cmd_irq, NULL);
+	free_irq(tpm_st_i2c_dev->pd->data_avail_irq, NULL);
+	tpm_remove_hardware(&client->dev);
+	tpm_st_i2c_dev->pd->gpio_release();
+	kfree(tpm_st_i2c_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id tpm_st_i2c_id[] = {
+	{ DEVICE_NAME, 0 },
+	{ }
+};
+
+static struct i2c_driver tpm_st_i2c_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = tpm_st_i2c_probe,
+	.remove =  __exit_p(tpm_st_i2c_remove),
+#ifdef CONFIG_PM
+	.suspend = tpm_st_i2c_suspend,
+	.resume = tpm_st_i2c_resume,
+#endif
+	.id_table = tpm_st_i2c_id,
+};
+
+static int __init tpm_st_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&tpm_st_i2c_driver);
+	if (ret)
+		printk(KERN_ERR "%s: failed to add i2c driver\n", __func__);
+
+	return ret;
+}
+
+static void __exit tpm_st_i2c_exit(void)
+{
+	i2c_del_driver(&tpm_st_i2c_driver);
+}
+
+module_init(tpm_st_i2c_init);
+module_exit(tpm_st_i2c_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
+MODULE_DESCRIPTION("ST19NP18-TPM-I2C driver");
diff --git a/drivers/char/tpm/tpmd_dev/Makefile b/drivers/char/tpm/tpmd_dev/Makefile
new file mode 100644
index 0000000..7d62de4
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the kernel tpm emulator device driver.
+#
+obj-$(CONFIG_TCG_TPM) += tpmd_dev.o
diff --git a/drivers/char/tpm/tpmd_dev/config.h b/drivers/char/tpm/tpmd_dev/config.h
new file mode 100644
index 0000000..ec8d93e
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/config.h
@@ -0,0 +1,32 @@
+/* Software-based Trusted Platform Module (TPM) Emulator
+ * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
+ *
+ * This module is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This module 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.
+ *
+ * $Id: config.h.in 426 2010-02-22 17:11:58Z mast $
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/* project and build version */
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 7
+#define VERSION_BUILD 424
+
+/* TDDL and LKM configuration */
+#define TPM_SOCKET_NAME  "/var/run/tpm/tpmd_socket:0"
+#define TPM_STORAGE_NAME "/var/lib/tpm/tpm_emulator-1_2_0_7"
+#define TPM_DEVICE_NAME  "/dev/tpm"
+#define TPM_LOG_FILE     ""
+#define TPM_CMD_BUF_SIZE 4096
+
+#endif /* _CONFIG_H_ */
diff --git a/drivers/char/tpm/tpmd_dev/tpmd_dev.c b/drivers/char/tpm/tpmd_dev/tpmd_dev.c
new file mode 100644
index 0000000..cbfcbd8
--- /dev/null
+++ b/drivers/char/tpm/tpmd_dev/tpmd_dev.c
@@ -0,0 +1,272 @@
+/* Software-based Trusted Platform Module (TPM) Emulator
+ * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
+ *
+ * This module is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This module 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.
+ *
+ * $Id: tpmd_dev.c 426 2010-02-22 17:11:58Z mast $
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/un.h>
+
+#include "config.h"
+
+#define TPM_DEVICE_MINOR  224
+#define TPM_DEVICE_ID     "tpm"
+#define TPM_MODULE_NAME   "tpmd_dev"
+
+#define TPM_STATE_IS_OPEN 0
+
+#ifdef DEBUG
+#define debug(fmt, ...) printk(KERN_DEBUG "%s %s:%d: Debug: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#else
+#define debug(fmt, ...)
+#endif
+#define info(fmt, ...)  printk(KERN_INFO "%s %s:%d: Info: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#define error(fmt, ...) printk(KERN_ERR "%s %s:%d: Error: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+#define alert(fmt, ...) printk(KERN_ALERT "%s %s:%d: Alert: " fmt "\n", \
+                        TPM_MODULE_NAME, __FILE__, __LINE__, ## __VA_ARGS__)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mario Strasser <mast@gmx.net>");
+MODULE_DESCRIPTION("Trusted Platform Module (TPM) Emulator");
+MODULE_SUPPORTED_DEVICE(TPM_DEVICE_ID);
+
+/* module parameters */
+char *tpmd_socket_name = TPM_SOCKET_NAME;
+module_param(tpmd_socket_name, charp, 0444);
+MODULE_PARM_DESC(tpmd_socket_name, " Sets the name of the TPM daemon socket.");
+
+/* TPM lock */
+static struct semaphore tpm_mutex;
+
+/* TPM command response */
+static struct {
+  uint8_t *data;
+  uint32_t size;
+} tpm_response;
+
+/* module state */
+static uint32_t module_state;
+static struct socket *tpmd_sock;
+static struct sockaddr_un addr;
+
+static int tpmd_connect(char *socket_name)
+{
+  int res;
+  res = sock_create(PF_UNIX, SOCK_STREAM, 0, &tpmd_sock);
+  if (res != 0) {
+    error("sock_create() failed: %d\n", res);
+    tpmd_sock = NULL;
+    return res;
+  }
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path));
+  res = tpmd_sock->ops->connect(tpmd_sock, 
+    (struct sockaddr*)&addr, sizeof(struct sockaddr_un), 0);
+  if (res != 0) {
+    error("sock_connect() failed: %d\n", res);
+    tpmd_sock->ops->release(tpmd_sock);
+    tpmd_sock = NULL;
+    return res;
+  }
+  return 0;
+}
+
+static void tpmd_disconnect(void)
+{
+  if (tpmd_sock != NULL) tpmd_sock->ops->release(tpmd_sock);
+  tpmd_sock = NULL;
+}
+
+static int tpmd_handle_command(const uint8_t *in, uint32_t in_size)
+{
+  int res;
+  mm_segment_t oldmm;
+  struct msghdr msg;
+  struct iovec iov;
+  /* send command to tpmd */
+  memset(&msg, 0, sizeof(msg));
+  iov.iov_base = (void*)in;
+  iov.iov_len = in_size;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  res = sock_sendmsg(tpmd_sock, &msg, in_size);
+  if (res < 0) {
+    error("sock_sendmsg() failed: %d\n", res);
+    return res;
+  }
+  /* receive response from tpmd */
+  tpm_response.size = TPM_CMD_BUF_SIZE;
+  tpm_response.data = kmalloc(tpm_response.size, GFP_KERNEL);
+  if (tpm_response.data == NULL) return -1;
+  memset(&msg, 0, sizeof(msg));
+  iov.iov_base = (void*)tpm_response.data;
+  iov.iov_len = tpm_response.size;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  oldmm = get_fs();
+  set_fs(KERNEL_DS);
+  res = sock_recvmsg(tpmd_sock, &msg, tpm_response.size, 0);
+  set_fs(oldmm);
+  if (res < 0) {
+    error("sock_recvmsg() failed: %d\n", res);
+    tpm_response.data = NULL;
+    return res;
+  }
+  tpm_response.size = res;
+  return 0;
+}
+
+static int tpm_open(struct inode *inode, struct file *file)
+{
+  int res;
+  debug("%s()", __FUNCTION__);
+  if (test_and_set_bit(TPM_STATE_IS_OPEN, (void*)&module_state)) return -EBUSY;
+  down(&tpm_mutex);
+  res = tpmd_connect(tpmd_socket_name);
+  up(&tpm_mutex);
+  if (res != 0) {
+    clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state);
+    return -EIO;
+  }
+  return 0;
+}
+
+static int tpm_release(struct inode *inode, struct file *file)
+{
+  debug("%s()", __FUNCTION__);
+  down(&tpm_mutex);
+  if (tpm_response.data != NULL) {
+    kfree(tpm_response.data);
+    tpm_response.data = NULL;
+  }
+  tpmd_disconnect();
+  up(&tpm_mutex);
+  clear_bit(TPM_STATE_IS_OPEN, (void*)&module_state);
+  return 0;
+}
+
+static ssize_t tpm_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+  debug("%s(%zd)", __FUNCTION__, count);
+  down(&tpm_mutex);
+  if (tpm_response.data != NULL) {
+    count = min(count, (size_t)tpm_response.size - (size_t)*ppos);
+    count -= copy_to_user(buf, &tpm_response.data[*ppos], count);
+    *ppos += count;
+    if ((size_t)tpm_response.size == (size_t)*ppos) {
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    }
+  } else {
+    count = 0;
+  }
+  up(&tpm_mutex);
+  return count;
+}
+
+static ssize_t tpm_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+  debug("%s(%zd)", __FUNCTION__, count);
+  down(&tpm_mutex);
+  *ppos = 0;
+  if (tpm_response.data != NULL) {
+    kfree(tpm_response.data);
+    tpm_response.data = NULL;
+  }
+  if (tpmd_handle_command(buf, count) != 0) { 
+    count = -EILSEQ;
+    tpm_response.data = NULL;
+  }
+  up(&tpm_mutex);
+  return count;
+}
+
+#define TPMIOC_CANCEL   _IO('T', 0x00)
+#define TPMIOC_TRANSMIT _IO('T', 0x01)
+
+static int tpm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+  debug("%s(%d, %p)", __FUNCTION__, cmd, (char*)arg);
+  if (cmd == TPMIOC_TRANSMIT) {
+    uint32_t count = ntohl(*(uint32_t*)(arg + 2));
+    down(&tpm_mutex);
+    if (tpm_response.data != NULL) {
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    }
+    if (tpmd_handle_command((char*)arg, count) == 0) {
+      tpm_response.size -= copy_to_user((char*)arg, tpm_response.data, tpm_response.size);
+      kfree(tpm_response.data);
+      tpm_response.data = NULL;
+    } else {
+      tpm_response.size = 0;
+      tpm_response.data = NULL;
+    }
+    up(&tpm_mutex);
+    return tpm_response.size;
+  }
+  return -1;
+}
+
+struct file_operations fops = {
+  .owner   = THIS_MODULE,
+  .open    = tpm_open,
+  .release = tpm_release,
+  .read    = tpm_read,
+  .write   = tpm_write,
+  .ioctl   = tpm_ioctl,
+};
+
+static struct miscdevice tpm_dev = {
+  .minor      = TPM_DEVICE_MINOR, 
+  .name       = TPM_DEVICE_ID, 
+  .fops       = &fops,
+};
+
+int __init init_tpm_module(void)
+{
+  int res = misc_register(&tpm_dev);
+  if (res != 0) {
+    error("misc_register() failed for minor %d\n", TPM_DEVICE_MINOR);
+    return res;
+  }
+  /* initialize variables */
+  sema_init(&tpm_mutex, 1);
+  module_state = 0;
+  tpm_response.data = NULL;
+  tpm_response.size = 0;
+  tpmd_sock = NULL;
+  return 0;
+}
+
+void __exit cleanup_tpm_module(void)
+{
+  misc_deregister(&tpm_dev);
+  tpmd_disconnect();
+  if (tpm_response.data != NULL) kfree(tpm_response.data);
+}
+
+module_init(init_tpm_module);
+module_exit(cleanup_tpm_module);
+
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
new file mode 100644
index 0000000..94bb440
--- /dev/null
+++ b/drivers/char/tty_io.c
@@ -0,0 +1,3154 @@
+/*
+ *  linux/drivers/char/tty_io.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
+ * or rs-channels. It also implements echoing, cooked mode etc.
+ *
+ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
+ *
+ * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
+ * tty_struct and tty_queue structures.  Previously there was an array
+ * of 256 tty_struct's which was statically allocated, and the
+ * tty_queue structures were allocated at boot time.  Both are now
+ * dynamically allocated only when the tty is open.
+ *
+ * Also restructured routines so that there is more of a separation
+ * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
+ * the low-level tty routines (serial.c, pty.c, console.c).  This
+ * makes for cleaner and more compact code.  -TYT, 9/17/92
+ *
+ * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
+ * which can be dynamically activated and de-activated by the line
+ * discipline handling modules (like SLIP).
+ *
+ * NOTE: pay no attention to the line discipline code (yet); its
+ * interface is still subject to change in this version...
+ * -- TYT, 1/31/92
+ *
+ * Added functionality to the OPOST tty handling.  No delays, but all
+ * other bits should be there.
+ *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
+ *
+ * Rewrote canonical mode and added more termios flags.
+ * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
+ *
+ * Reorganized FASYNC support so mouse code can share it.
+ *	-- ctm@ardi.com, 9Sep95
+ *
+ * New TIOCLINUX variants added.
+ *	-- mj@k332.feld.cvut.cz, 19-Nov-95
+ *
+ * Restrict vt switching via ioctl()
+ *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Move console and virtual terminal code to more appropriate files,
+ * implement CONFIG_VT and generalize console device interface.
+ *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote tty_init_dev and tty_release_dev to eliminate races.
+ *	-- Bill Hawes <whawes@star.net>, June 97
+ *
+ * Added devfs support.
+ *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
+ *
+ * Added support for a Unix98-style ptmx device.
+ *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
+ *
+ * Reduced memory usage for older ARM systems
+ *      -- Russell King <rmk@arm.linux.org.uk>
+ *
+ * Move do_SAK() into process context.  Less stack use in devfs functions.
+ * alloc_tty_struct() always uses kmalloc()
+ *			 -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/console.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+
+#include <linux/kmod.h>
+#include <linux/nsproxy.h>
+
+#undef TTY_DEBUG_HANGUP
+
+#define TTY_PARANOIA_CHECK 1
+#define CHECK_TTY_COUNT 1
+
+struct ktermios tty_std_termios = {	/* for the benefit of tty drivers  */
+	.c_iflag = ICRNL | IXON,
+	.c_oflag = OPOST | ONLCR,
+	.c_cflag = B38400 | CS8 | CREAD | HUPCL,
+	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
+		   ECHOCTL | ECHOKE | IEXTEN,
+	.c_cc = INIT_C_CC,
+	.c_ispeed = 38400,
+	.c_ospeed = 38400
+};
+
+EXPORT_SYMBOL(tty_std_termios);
+
+/* This list gets poked at by procfs and various bits of boot up code. This
+   could do with some rationalisation such as pulling the tty proc function
+   into this file */
+
+LIST_HEAD(tty_drivers);			/* linked list of tty drivers */
+
+/* Mutex to protect creating and releasing a tty. This is shared with
+   vt.c for deeply disgusting hack reasons */
+DEFINE_MUTEX(tty_mutex);
+EXPORT_SYMBOL(tty_mutex);
+
+static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
+ssize_t redirected_tty_write(struct file *, const char __user *,
+							size_t, loff_t *);
+static unsigned int tty_poll(struct file *, poll_table *);
+static int tty_open(struct inode *, struct file *);
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg);
+#else
+#define tty_compat_ioctl NULL
+#endif
+static int tty_fasync(int fd, struct file *filp, int on);
+static void release_tty(struct tty_struct *tty, int idx);
+static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+
+/**
+ *	alloc_tty_struct	-	allocate a tty object
+ *
+ *	Return a new empty tty structure. The data fields have not
+ *	been initialized in any way but has been zeroed
+ *
+ *	Locking: none
+ */
+
+struct tty_struct *alloc_tty_struct(void)
+{
+	return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
+}
+
+/**
+ *	free_tty_struct		-	free a disused tty
+ *	@tty: tty struct to free
+ *
+ *	Free the write buffers, tty queue and tty memory itself.
+ *
+ *	Locking: none. Must be called after tty is definitely unused
+ */
+
+void free_tty_struct(struct tty_struct *tty)
+{
+	kfree(tty->write_buf);
+	tty_buffer_free_all(tty);
+	kfree(tty);
+}
+
+#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
+
+/**
+ *	tty_name	-	return tty naming
+ *	@tty: tty structure
+ *	@buf: buffer for output
+ *
+ *	Convert a tty structure into a name. The name reflects the kernel
+ *	naming policy and if udev is in use may not reflect user space
+ *
+ *	Locking: none
+ */
+
+char *tty_name(struct tty_struct *tty, char *buf)
+{
+	if (!tty) /* Hmm.  NULL pointer.  That's fun. */
+		strcpy(buf, "NULL tty");
+	else
+		strcpy(buf, tty->name);
+	return buf;
+}
+
+EXPORT_SYMBOL(tty_name);
+
+int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	if (!tty) {
+		printk(KERN_WARNING
+			"null TTY for (%d:%d) in %s\n",
+			imajor(inode), iminor(inode), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(KERN_WARNING
+			"bad magic number for tty struct (%d:%d) in %s\n",
+			imajor(inode), iminor(inode), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+	struct list_head *p;
+	int count = 0;
+
+	file_list_lock();
+	list_for_each(p, &tty->tty_files) {
+		count++;
+	}
+	file_list_unlock();
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_SLAVE &&
+	    tty->link && tty->link->count)
+		count++;
+	if (tty->count != count) {
+		printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
+				    "!= #fd's(%d) in %s\n",
+		       tty->name, tty->count, count, routine);
+		return count;
+	}
+#endif
+	return 0;
+}
+
+/**
+ *	get_tty_driver		-	find device of a tty
+ *	@dev_t: device identifier
+ *	@index: returns the index of the tty
+ *
+ *	This routine returns a tty driver structure, given a device number
+ *	and also passes back the index number.
+ *
+ *	Locking: caller must hold tty_mutex
+ */
+
+static struct tty_driver *get_tty_driver(dev_t device, int *index)
+{
+	struct tty_driver *p;
+
+	list_for_each_entry(p, &tty_drivers, tty_drivers) {
+		dev_t base = MKDEV(p->major, p->minor_start);
+		if (device < base || device >= base + p->num)
+			continue;
+		*index = device - base;
+		return tty_driver_kref_get(p);
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+/**
+ *	tty_find_polling_driver	-	find device of a polled tty
+ *	@name: name string to match
+ *	@line: pointer to resulting tty line nr
+ *
+ *	This routine returns a tty driver structure, given a name
+ *	and the condition that the tty driver is capable of polled
+ *	operation.
+ */
+struct tty_driver *tty_find_polling_driver(char *name, int *line)
+{
+	struct tty_driver *p, *res = NULL;
+	int tty_line = 0;
+	int len;
+	char *str, *stp;
+
+	for (str = name; *str; str++)
+		if ((*str >= '0' && *str <= '9') || *str == ',')
+			break;
+	if (!*str)
+		return NULL;
+
+	len = str - name;
+	tty_line = simple_strtoul(str, &str, 10);
+
+	mutex_lock(&tty_mutex);
+	/* Search through the tty devices to look for a match */
+	list_for_each_entry(p, &tty_drivers, tty_drivers) {
+		if (strncmp(name, p->name, len) != 0)
+			continue;
+		stp = str;
+		if (*stp == ',')
+			stp++;
+		if (*stp == '\0')
+			stp = NULL;
+
+		if (tty_line >= 0 && tty_line <= p->num && p->ops &&
+		    p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
+			res = tty_driver_kref_get(p);
+			*line = tty_line;
+			break;
+		}
+	}
+	mutex_unlock(&tty_mutex);
+
+	return res;
+}
+EXPORT_SYMBOL_GPL(tty_find_polling_driver);
+#endif
+
+/**
+ *	tty_check_change	-	check for POSIX terminal changes
+ *	@tty: tty to check
+ *
+ *	If we try to write to, or set the state of, a terminal and we're
+ *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
+ *	ignored, go ahead and perform the operation.  (POSIX 7.2)
+ *
+ *	Locking: ctrl_lock
+ */
+
+int tty_check_change(struct tty_struct *tty)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	if (current->signal->tty != tty)
+		return 0;
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+
+	if (!tty->pgrp) {
+		printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
+		goto out_unlock;
+	}
+	if (task_pgrp(current) == tty->pgrp)
+		goto out_unlock;
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (is_ignored(SIGTTOU))
+		goto out;
+	if (is_current_pgrp_orphaned()) {
+		ret = -EIO;
+		goto out;
+	}
+	kill_pgrp(task_pgrp(current), SIGTTOU, 1);
+	set_thread_flag(TIF_SIGPENDING);
+	ret = -ERESTARTSYS;
+out:
+	return ret;
+out_unlock:
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	return ret;
+}
+
+EXPORT_SYMBOL(tty_check_change);
+
+static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	return -EIO;
+}
+
+/* No kernel lock held - none needed ;) */
+static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
+{
+	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
+}
+
+static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
+}
+
+static long hung_up_tty_compat_ioctl(struct file *file,
+				     unsigned int cmd, unsigned long arg)
+{
+	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
+}
+
+static const struct file_operations tty_fops = {
+	.llseek		= no_llseek,
+	.read		= tty_read,
+	.write		= tty_write,
+	.poll		= tty_poll,
+	.unlocked_ioctl	= tty_ioctl,
+	.compat_ioctl	= tty_compat_ioctl,
+	.open		= tty_open,
+	.release	= tty_release,
+	.fasync		= tty_fasync,
+};
+
+static const struct file_operations console_fops = {
+	.llseek		= no_llseek,
+	.read		= tty_read,
+	.write		= redirected_tty_write,
+	.poll		= tty_poll,
+	.unlocked_ioctl	= tty_ioctl,
+	.compat_ioctl	= tty_compat_ioctl,
+	.open		= tty_open,
+	.release	= tty_release,
+	.fasync		= tty_fasync,
+};
+
+static const struct file_operations hung_up_tty_fops = {
+	.llseek		= no_llseek,
+	.read		= hung_up_tty_read,
+	.write		= hung_up_tty_write,
+	.poll		= hung_up_tty_poll,
+	.unlocked_ioctl	= hung_up_tty_ioctl,
+	.compat_ioctl	= hung_up_tty_compat_ioctl,
+	.release	= tty_release,
+};
+
+static DEFINE_SPINLOCK(redirect_lock);
+static struct file *redirect;
+
+/**
+ *	tty_wakeup	-	request more data
+ *	@tty: terminal
+ *
+ *	Internal and external helper for wakeups of tty. This function
+ *	informs the line discipline if present that the driver is ready
+ *	to receive more output data.
+ */
+
+void tty_wakeup(struct tty_struct *tty)
+{
+	struct tty_ldisc *ld;
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
+		ld = tty_ldisc_ref(tty);
+		if (ld) {
+			if (ld->ops->write_wakeup)
+				ld->ops->write_wakeup(tty);
+			tty_ldisc_deref(ld);
+		}
+	}
+	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+EXPORT_SYMBOL_GPL(tty_wakeup);
+
+/**
+ *	do_tty_hangup		-	actual handler for hangup events
+ *	@work: tty device
+ *
+ *	This can be called by the "eventd" kernel thread.  That is process
+ *	synchronous but doesn't hold any locks, so we need to make sure we
+ *	have the appropriate locks for what we're doing.
+ *
+ *	The hangup event clears any pending redirections onto the hung up
+ *	device. It ensures future writes will error and it does the needed
+ *	line discipline hangup and signal delivery. The tty object itself
+ *	remains intact.
+ *
+ *	Locking:
+ *		BKL
+ *		  redirect lock for undoing redirection
+ *		  file list lock for manipulating list of ttys
+ *		  tty_ldisc_lock from called functions
+ *		  termios_mutex resetting termios data
+ *		  tasklist_lock to walk task list for hangup event
+ *		    ->siglock to protect ->signal/->sighand
+ */
+static void do_tty_hangup(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
+	struct file *cons_filp = NULL;
+	struct file *filp, *f = NULL;
+	struct task_struct *p;
+	int    closecount = 0, n;
+	unsigned long flags;
+	int refs = 0;
+
+	if (!tty)
+		return;
+
+
+	spin_lock(&redirect_lock);
+	if (redirect && redirect->private_data == tty) {
+		f = redirect;
+		redirect = NULL;
+	}
+	spin_unlock(&redirect_lock);
+
+	/* inuse_filps is protected by the single kernel lock */
+	lock_kernel();
+	check_tty_count(tty, "do_tty_hangup");
+
+	file_list_lock();
+	/* This breaks for file handles being sent over AF_UNIX sockets ? */
+	list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
+		if (filp->f_op->write == redirected_tty_write)
+			cons_filp = filp;
+		if (filp->f_op->write != tty_write)
+			continue;
+		closecount++;
+		tty_fasync(-1, filp, 0);	/* can't block */
+		filp->f_op = &hung_up_tty_fops;
+	}
+	file_list_unlock();
+
+	tty_ldisc_hangup(tty);
+
+	read_lock(&tasklist_lock);
+	if (tty->session) {
+		do_each_pid_task(tty->session, PIDTYPE_SID, p) {
+			spin_lock_irq(&p->sighand->siglock);
+			if (p->signal->tty == tty) {
+				p->signal->tty = NULL;
+				/* We defer the dereferences outside fo
+				   the tasklist lock */
+				refs++;
+			}
+			if (!p->signal->leader) {
+				spin_unlock_irq(&p->sighand->siglock);
+				continue;
+			}
+			__group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+			__group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+			put_pid(p->signal->tty_old_pgrp);  /* A noop */
+			spin_lock_irqsave(&tty->ctrl_lock, flags);
+			if (tty->pgrp)
+				p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+			spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+			spin_unlock_irq(&p->sighand->siglock);
+		} while_each_pid_task(tty->session, PIDTYPE_SID, p);
+	}
+	read_unlock(&tasklist_lock);
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	clear_bit(TTY_THROTTLED, &tty->flags);
+	clear_bit(TTY_PUSH, &tty->flags);
+	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	put_pid(tty->session);
+	put_pid(tty->pgrp);
+	tty->session = NULL;
+	tty->pgrp = NULL;
+	tty->ctrl_status = 0;
+	set_bit(TTY_HUPPED, &tty->flags);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	/* Account for the p->signal references we killed */
+	while (refs--)
+		tty_kref_put(tty);
+
+	/*
+	 * If one of the devices matches a console pointer, we
+	 * cannot just call hangup() because that will cause
+	 * tty->count and state->count to go out of sync.
+	 * So we just call close() the right number of times.
+	 */
+	if (cons_filp) {
+		if (tty->ops->close)
+			for (n = 0; n < closecount; n++)
+				tty->ops->close(tty, cons_filp);
+	} else if (tty->ops->hangup)
+		(tty->ops->hangup)(tty);
+	/*
+	 * We don't want to have driver/ldisc interactions beyond
+	 * the ones we did here. The driver layer expects no
+	 * calls after ->hangup() from the ldisc side. However we
+	 * can't yet guarantee all that.
+	 */
+	set_bit(TTY_HUPPED, &tty->flags);
+	tty_ldisc_enable(tty);
+	unlock_kernel();
+	if (f)
+		fput(f);
+}
+
+/**
+ *	tty_hangup		-	trigger a hangup event
+ *	@tty: tty to hangup
+ *
+ *	A carrier loss (virtual or otherwise) has occurred on this like
+ *	schedule a hangup sequence to run after this event.
+ */
+
+void tty_hangup(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	char	buf[64];
+	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
+#endif
+	schedule_work(&tty->hangup_work);
+}
+
+EXPORT_SYMBOL(tty_hangup);
+
+/**
+ *	tty_vhangup		-	process vhangup
+ *	@tty: tty to hangup
+ *
+ *	The user has asked via system call for the terminal to be hung up.
+ *	We do this synchronously so that when the syscall returns the process
+ *	is complete. That guarantee is necessary for security reasons.
+ */
+
+void tty_vhangup(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	char	buf[64];
+
+	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
+#endif
+	do_tty_hangup(&tty->hangup_work);
+}
+
+EXPORT_SYMBOL(tty_vhangup);
+
+/**
+ *	tty_vhangup_self	-	process vhangup for own ctty
+ *
+ *	Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+	struct tty_struct *tty;
+
+	tty = get_current_tty();
+	if (tty) {
+		tty_vhangup(tty);
+		tty_kref_put(tty);
+	}
+}
+
+/**
+ *	tty_hung_up_p		-	was tty hung up
+ *	@filp: file pointer of tty
+ *
+ *	Return true if the tty has been subject to a vhangup or a carrier
+ *	loss
+ */
+
+int tty_hung_up_p(struct file *filp)
+{
+	return (filp->f_op == &hung_up_tty_fops);
+}
+
+EXPORT_SYMBOL(tty_hung_up_p);
+
+static void session_clear_tty(struct pid *session)
+{
+	struct task_struct *p;
+	do_each_pid_task(session, PIDTYPE_SID, p) {
+		proc_clear_tty(p);
+	} while_each_pid_task(session, PIDTYPE_SID, p);
+}
+
+/**
+ *	disassociate_ctty	-	disconnect controlling tty
+ *	@on_exit: true if exiting so need to "hang up" the session
+ *
+ *	This function is typically called only by the session leader, when
+ *	it wants to disassociate itself from its controlling tty.
+ *
+ *	It performs the following functions:
+ * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
+ * 	(2)  Clears the tty from being controlling the session
+ * 	(3)  Clears the controlling tty for all processes in the
+ * 		session group.
+ *
+ *	The argument on_exit is set to 1 if called when a process is
+ *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
+ *
+ *	Locking:
+ *		BKL is taken for hysterical raisins
+ *		  tty_mutex is taken to protect tty
+ *		  ->siglock is taken to protect ->signal/->sighand
+ *		  tasklist_lock is taken to walk process list for sessions
+ *		    ->siglock is taken to protect ->signal/->sighand
+ */
+
+void disassociate_ctty(int on_exit)
+{
+	struct tty_struct *tty;
+	struct pid *tty_pgrp = NULL;
+
+	if (!current->signal->leader)
+		return;
+
+	tty = get_current_tty();
+	if (tty) {
+		tty_pgrp = get_pid(tty->pgrp);
+		lock_kernel();
+		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
+			tty_vhangup(tty);
+		unlock_kernel();
+		tty_kref_put(tty);
+	} else if (on_exit) {
+		struct pid *old_pgrp;
+		spin_lock_irq(&current->sighand->siglock);
+		old_pgrp = current->signal->tty_old_pgrp;
+		current->signal->tty_old_pgrp = NULL;
+		spin_unlock_irq(&current->sighand->siglock);
+		if (old_pgrp) {
+			kill_pgrp(old_pgrp, SIGHUP, on_exit);
+			kill_pgrp(old_pgrp, SIGCONT, on_exit);
+			put_pid(old_pgrp);
+		}
+		return;
+	}
+	if (tty_pgrp) {
+		kill_pgrp(tty_pgrp, SIGHUP, on_exit);
+		if (!on_exit)
+			kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+		put_pid(tty_pgrp);
+	}
+
+	spin_lock_irq(&current->sighand->siglock);
+	put_pid(current->signal->tty_old_pgrp);
+	current->signal->tty_old_pgrp = NULL;
+	spin_unlock_irq(&current->sighand->siglock);
+
+	tty = get_current_tty();
+	if (tty) {
+		unsigned long flags;
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		put_pid(tty->session);
+		put_pid(tty->pgrp);
+		tty->session = NULL;
+		tty->pgrp = NULL;
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		tty_kref_put(tty);
+	} else {
+#ifdef TTY_DEBUG_HANGUP
+		printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
+		       " = NULL", tty);
+#endif
+	}
+
+	/* Now clear signal->tty under the lock */
+	read_lock(&tasklist_lock);
+	session_clear_tty(task_session(current));
+	read_unlock(&tasklist_lock);
+}
+
+/**
+ *
+ *	no_tty	- Ensure the current process does not have a controlling tty
+ */
+void no_tty(void)
+{
+	struct task_struct *tsk = current;
+	lock_kernel();
+	disassociate_ctty(0);
+	unlock_kernel();
+	proc_clear_tty(tsk);
+}
+
+
+/**
+ *	stop_tty	-	propagate flow control
+ *	@tty: tty to stop
+ *
+ *	Perform flow control to the driver. For PTY/TTY pairs we
+ *	must also propagate the TIOCKPKT status. May be called
+ *	on an already stopped device and will not re-call the driver
+ *	method.
+ *
+ *	This functionality is used by both the line disciplines for
+ *	halting incoming flow and by the driver. It may therefore be
+ *	called from any context, may be under the tty atomic_write_lock
+ *	but not always.
+ *
+ *	Locking:
+ *		Uses the tty control lock internally
+ */
+
+void stop_tty(struct tty_struct *tty)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	if (tty->stopped) {
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		return;
+	}
+	tty->stopped = 1;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_START;
+		tty->ctrl_status |= TIOCPKT_STOP;
+		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
+	}
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (tty->ops->stop)
+		(tty->ops->stop)(tty);
+}
+
+EXPORT_SYMBOL(stop_tty);
+
+/**
+ *	start_tty	-	propagate flow control
+ *	@tty: tty to start
+ *
+ *	Start a tty that has been stopped if at all possible. Perform
+ *	any necessary wakeups and propagate the TIOCPKT status. If this
+ *	is the tty was previous stopped and is being started then the
+ *	driver start method is invoked and the line discipline woken.
+ *
+ *	Locking:
+ *		ctrl_lock
+ */
+
+void start_tty(struct tty_struct *tty)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	if (!tty->stopped || tty->flow_stopped) {
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		return;
+	}
+	tty->stopped = 0;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_STOP;
+		tty->ctrl_status |= TIOCPKT_START;
+		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
+	}
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+	if (tty->ops->start)
+		(tty->ops->start)(tty);
+	/* If we have a running line discipline it may need kicking */
+	tty_wakeup(tty);
+}
+
+EXPORT_SYMBOL(start_tty);
+
+/**
+ *	tty_read	-	read method for tty device files
+ *	@file: pointer to tty file
+ *	@buf: user buffer
+ *	@count: size of user buffer
+ *	@ppos: unused
+ *
+ *	Perform the read system call function on this terminal device. Checks
+ *	for hung up devices before calling the line discipline method.
+ *
+ *	Locking:
+ *		Locks the line discipline internally while needed. Multiple
+ *	read calls may be outstanding in parallel.
+ */
+
+static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
+			loff_t *ppos)
+{
+	int i;
+	struct tty_struct *tty;
+	struct inode *inode;
+	struct tty_ldisc *ld;
+
+	tty = (struct tty_struct *)file->private_data;
+	inode = file->f_path.dentry->d_inode;
+	if (tty_paranoia_check(tty, inode, "tty_read"))
+		return -EIO;
+	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
+		return -EIO;
+
+	/* We want to wait for the line discipline to sort out in this
+	   situation */
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->read)
+		i = (ld->ops->read)(tty, file, buf, count);
+	else
+		i = -EIO;
+	tty_ldisc_deref(ld);
+	if (i > 0)
+		inode->i_atime = current_fs_time(inode->i_sb);
+	return i;
+}
+
+void tty_write_unlock(struct tty_struct *tty)
+{
+	mutex_unlock(&tty->atomic_write_lock);
+	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+}
+
+int tty_write_lock(struct tty_struct *tty, int ndelay)
+{
+	if (!mutex_trylock(&tty->atomic_write_lock)) {
+		if (ndelay)
+			return -EAGAIN;
+		if (mutex_lock_interruptible(&tty->atomic_write_lock))
+			return -ERESTARTSYS;
+	}
+	return 0;
+}
+
+/*
+ * Split writes up in sane blocksizes to avoid
+ * denial-of-service type attacks
+ */
+static inline ssize_t do_tty_write(
+	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
+	struct tty_struct *tty,
+	struct file *file,
+	const char __user *buf,
+	size_t count)
+{
+	ssize_t ret, written = 0;
+	unsigned int chunk;
+
+	ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * We chunk up writes into a temporary buffer. This
+	 * simplifies low-level drivers immensely, since they
+	 * don't have locking issues and user mode accesses.
+	 *
+	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
+	 * big chunk-size..
+	 *
+	 * The default chunk-size is 2kB, because the NTTY
+	 * layer has problems with bigger chunks. It will
+	 * claim to be able to handle more characters than
+	 * it actually does.
+	 *
+	 * FIXME: This can probably go away now except that 64K chunks
+	 * are too likely to fail unless switched to vmalloc...
+	 */
+	chunk = 2048;
+	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
+		chunk = 65536;
+	if (count < chunk)
+		chunk = count;
+
+	/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
+	if (tty->write_cnt < chunk) {
+		unsigned char *buf_chunk;
+
+		if (chunk < 1024)
+			chunk = 1024;
+
+		buf_chunk = kmalloc(chunk, GFP_KERNEL);
+		if (!buf_chunk) {
+			ret = -ENOMEM;
+			goto out;
+		}
+		kfree(tty->write_buf);
+		tty->write_cnt = chunk;
+		tty->write_buf = buf_chunk;
+	}
+
+	/* Do the write .. */
+	for (;;) {
+		size_t size = count;
+		if (size > chunk)
+			size = chunk;
+		ret = -EFAULT;
+		if (copy_from_user(tty->write_buf, buf, size))
+			break;
+		ret = write(tty, file, tty->write_buf, size);
+		if (ret <= 0)
+			break;
+		written += ret;
+		buf += ret;
+		count -= ret;
+		if (!count)
+			break;
+		ret = -ERESTARTSYS;
+		if (signal_pending(current))
+			break;
+		cond_resched();
+	}
+	if (written) {
+		struct inode *inode = file->f_path.dentry->d_inode;
+		inode->i_mtime = current_fs_time(inode->i_sb);
+		ret = written;
+	}
+out:
+	tty_write_unlock(tty);
+	return ret;
+}
+
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+	if (tty) {
+		mutex_lock(&tty->atomic_write_lock);
+		lock_kernel();
+		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+			unlock_kernel();
+			tty->ops->write(tty, msg, strlen(msg));
+		} else
+			unlock_kernel();
+		tty_write_unlock(tty);
+	}
+	return;
+}
+
+
+/**
+ *	tty_write		-	write method for tty device file
+ *	@file: tty file pointer
+ *	@buf: user data to write
+ *	@count: bytes to write
+ *	@ppos: unused
+ *
+ *	Write data to a tty device via the line discipline.
+ *
+ *	Locking:
+ *		Locks the line discipline as required
+ *		Writes to the tty driver are serialized by the atomic_write_lock
+ *	and are then processed in chunks to the device. The line discipline
+ *	write method will not be invoked in parallel for each device.
+ */
+
+static ssize_t tty_write(struct file *file, const char __user *buf,
+						size_t count, loff_t *ppos)
+{
+	struct tty_struct *tty;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	ssize_t ret;
+	struct tty_ldisc *ld;
+
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_write"))
+		return -EIO;
+	if (!tty || !tty->ops->write ||
+		(test_bit(TTY_IO_ERROR, &tty->flags)))
+			return -EIO;
+	/* Short term debug to catch buggy drivers */
+	if (tty->ops->write_room == NULL)
+		printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
+			tty->driver->name);
+	ld = tty_ldisc_ref_wait(tty);
+	if (!ld->ops->write)
+		ret = -EIO;
+	else
+		ret = do_tty_write(ld->ops->write, tty, file, buf, count);
+	tty_ldisc_deref(ld);
+	return ret;
+}
+
+ssize_t redirected_tty_write(struct file *file, const char __user *buf,
+						size_t count, loff_t *ppos)
+{
+	struct file *p = NULL;
+
+	spin_lock(&redirect_lock);
+	if (redirect) {
+		get_file(redirect);
+		p = redirect;
+	}
+	spin_unlock(&redirect_lock);
+
+	if (p) {
+		ssize_t res;
+		res = vfs_write(p, buf, count, &p->f_pos);
+		fput(p);
+		return res;
+	}
+	return tty_write(file, buf, count, ppos);
+}
+
+static char ptychar[] = "pqrstuvwxyzabcde";
+
+/**
+ *	pty_line_name	-	generate name for a pty
+ *	@driver: the tty driver in use
+ *	@index: the minor number
+ *	@p: output buffer of at least 6 bytes
+ *
+ *	Generate a name from a driver reference and write it to the output
+ *	buffer.
+ *
+ *	Locking: None
+ */
+static void pty_line_name(struct tty_driver *driver, int index, char *p)
+{
+	int i = index + driver->name_base;
+	/* ->name is initialized to "ttyp", but "tty" is expected */
+	sprintf(p, "%s%c%x",
+		driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
+		ptychar[i >> 4 & 0xf], i & 0xf);
+}
+
+/**
+ *	tty_line_name	-	generate name for a tty
+ *	@driver: the tty driver in use
+ *	@index: the minor number
+ *	@p: output buffer of at least 7 bytes
+ *
+ *	Generate a name from a driver reference and write it to the output
+ *	buffer.
+ *
+ *	Locking: None
+ */
+static void tty_line_name(struct tty_driver *driver, int index, char *p)
+{
+	sprintf(p, "%s%d", driver->name, index + driver->name_base);
+}
+
+/**
+ *	tty_driver_lookup_tty() - find an existing tty, if any
+ *	@driver: the driver for the tty
+ *	@idx:	 the minor number
+ *
+ *	Return the tty, if found or ERR_PTR() otherwise.
+ *
+ *	Locking: tty_mutex must be held. If tty is found, the mutex must
+ *	be held until the 'fast-open' is also done. Will change once we
+ *	have refcounting in the driver and per driver locking
+ */
+static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
+		struct inode *inode, int idx)
+{
+	struct tty_struct *tty;
+
+	if (driver->ops->lookup)
+		return driver->ops->lookup(driver, inode, idx);
+
+	tty = driver->ttys[idx];
+	return tty;
+}
+
+/**
+ *	tty_init_termios	-  helper for termios setup
+ *	@tty: the tty to set up
+ *
+ *	Initialise the termios structures for this tty. Thus runs under
+ *	the tty_mutex currently so we can be relaxed about ordering.
+ */
+
+int tty_init_termios(struct tty_struct *tty)
+{
+	struct ktermios *tp;
+	int idx = tty->index;
+
+	tp = tty->driver->termios[idx];
+	if (tp == NULL) {
+		tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+		if (tp == NULL)
+			return -ENOMEM;
+		memcpy(tp, &tty->driver->init_termios,
+						sizeof(struct ktermios));
+		tty->driver->termios[idx] = tp;
+	}
+	tty->termios = tp;
+	tty->termios_locked = tp + 1;
+
+	/* Compatibility until drivers always set this */
+	tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+	tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tty_init_termios);
+
+/**
+ *	tty_driver_install_tty() - install a tty entry in the driver
+ *	@driver: the driver for the tty
+ *	@tty: the tty
+ *
+ *	Install a tty object into the driver tables. The tty->index field
+ *	will be set by the time this is called. This method is responsible
+ *	for ensuring any need additional structures are allocated and
+ *	configured.
+ *
+ *	Locking: tty_mutex for now
+ */
+static int tty_driver_install_tty(struct tty_driver *driver,
+						struct tty_struct *tty)
+{
+	int idx = tty->index;
+	int ret;
+
+	if (driver->ops->install) {
+		lock_kernel();
+		ret = driver->ops->install(driver, tty);
+		unlock_kernel();
+		return ret;
+	}
+
+	if (tty_init_termios(tty) == 0) {
+		lock_kernel();
+		tty_driver_kref_get(driver);
+		tty->count++;
+		driver->ttys[idx] = tty;
+		unlock_kernel();
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	tty_driver_remove_tty() - remove a tty from the driver tables
+ *	@driver: the driver for the tty
+ *	@idx:	 the minor number
+ *
+ *	Remvoe a tty object from the driver tables. The tty->index field
+ *	will be set by the time this is called.
+ *
+ *	Locking: tty_mutex for now
+ */
+static void tty_driver_remove_tty(struct tty_driver *driver,
+						struct tty_struct *tty)
+{
+	if (driver->ops->remove)
+		driver->ops->remove(driver, tty);
+	else
+		driver->ttys[tty->index] = NULL;
+}
+
+/*
+ * 	tty_reopen()	- fast re-open of an open tty
+ * 	@tty	- the tty to open
+ *
+ *	Return 0 on success, -errno on error.
+ *
+ *	Locking: tty_mutex must be held from the time the tty was found
+ *		 till this open completes.
+ */
+static int tty_reopen(struct tty_struct *tty)
+{
+	struct tty_driver *driver = tty->driver;
+
+	if (test_bit(TTY_CLOSING, &tty->flags))
+		return -EIO;
+
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER) {
+		/*
+		 * special case for PTY masters: only one open permitted,
+		 * and the slave side open count is incremented as well.
+		 */
+		if (tty->count)
+			return -EIO;
+
+		tty->link->count++;
+	}
+	tty->count++;
+	tty->driver = driver; /* N.B. why do this every time?? */
+
+	mutex_lock(&tty->ldisc_mutex);
+	WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+	mutex_unlock(&tty->ldisc_mutex);
+
+	return 0;
+}
+
+/**
+ *	tty_init_dev		-	initialise a tty device
+ *	@driver: tty driver we are opening a device on
+ *	@idx: device index
+ *	@ret_tty: returned tty structure
+ *	@first_ok: ok to open a new device (used by ptmx)
+ *
+ *	Prepare a tty device. This may not be a "new" clean device but
+ *	could also be an active device. The pty drivers require special
+ *	handling because of this.
+ *
+ *	Locking:
+ *		The function is called under the tty_mutex, which
+ *	protects us from the tty struct or driver itself going away.
+ *
+ *	On exit the tty device has the line discipline attached and
+ *	a reference count of 1. If a pair was created for pty/tty use
+ *	and the other was a pty master then it too has a reference count of 1.
+ *
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
+ * failed open.  The new code protects the open with a mutex, so it's
+ * really quite straightforward.  The mutex locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
+ */
+
+struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+								int first_ok)
+{
+	struct tty_struct *tty;
+	int retval;
+
+	lock_kernel();
+	/* Check if pty master is being opened multiple times */
+	if (driver->subtype == PTY_TYPE_MASTER &&
+		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+		unlock_kernel();
+		return ERR_PTR(-EIO);
+	}
+	unlock_kernel();
+
+	/*
+	 * First time open is complex, especially for PTY devices.
+	 * This code guarantees that either everything succeeds and the
+	 * TTY is ready for operation, or else the table slots are vacated
+	 * and the allocated memory released.  (Except that the termios
+	 * and locked termios may be retained.)
+	 */
+
+	if (!try_module_get(driver->owner))
+		return ERR_PTR(-ENODEV);
+
+	tty = alloc_tty_struct();
+	if (!tty)
+		goto fail_no_mem;
+	initialize_tty_struct(tty, driver, idx);
+
+	retval = tty_driver_install_tty(driver, tty);
+	if (retval < 0) {
+		free_tty_struct(tty);
+		module_put(driver->owner);
+		return ERR_PTR(retval);
+	}
+
+	/*
+	 * Structures all installed ... call the ldisc open routines.
+	 * If we fail here just call release_tty to clean up.  No need
+	 * to decrement the use counts, as release_tty doesn't care.
+	 */
+	retval = tty_ldisc_setup(tty, tty->link);
+	if (retval)
+		goto release_mem_out;
+	return tty;
+
+fail_no_mem:
+	module_put(driver->owner);
+	return ERR_PTR(-ENOMEM);
+
+	/* call the tty release_tty routine to clean out this slot */
+release_mem_out:
+	if (printk_ratelimit())
+		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
+				 "clearing slot %d\n", idx);
+	lock_kernel();
+	release_tty(tty, idx);
+	unlock_kernel();
+	return ERR_PTR(retval);
+}
+
+void tty_free_termios(struct tty_struct *tty)
+{
+	struct ktermios *tp;
+	int idx = tty->index;
+	/* Kill this flag and push into drivers for locking etc */
+	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+		/* FIXME: Locking on ->termios array */
+		tp = tty->termios;
+		tty->driver->termios[idx] = NULL;
+		kfree(tp);
+	}
+}
+EXPORT_SYMBOL(tty_free_termios);
+
+void tty_shutdown(struct tty_struct *tty)
+{
+	tty_driver_remove_tty(tty->driver, tty);
+	tty_free_termios(tty);
+}
+EXPORT_SYMBOL(tty_shutdown);
+
+/**
+ *	release_one_tty		-	release tty structure memory
+ *	@kref: kref of tty we are obliterating
+ *
+ *	Releases memory associated with a tty structure, and clears out the
+ *	driver table slots. This function is called when a device is no longer
+ *	in use. It also gets called when setup of a device fails.
+ *
+ *	Locking:
+ *		tty_mutex - sometimes only
+ *		takes the file list lock internally when working on the list
+ *	of ttys that the driver keeps.
+ *
+ *	This method gets called from a work queue so that the driver private
+ *	cleanup ops can sleep (needed for USB at least)
+ */
+static void release_one_tty(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, hangup_work);
+	struct tty_driver *driver = tty->driver;
+
+	if (tty->ops->cleanup)
+		tty->ops->cleanup(tty);
+
+	tty->magic = 0;
+	tty_driver_kref_put(driver);
+	module_put(driver->owner);
+
+	file_list_lock();
+	list_del_init(&tty->tty_files);
+	file_list_unlock();
+
+	put_pid(tty->pgrp);
+	put_pid(tty->session);
+	free_tty_struct(tty);
+}
+
+static void queue_release_one_tty(struct kref *kref)
+{
+	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+
+	if (tty->ops->shutdown)
+		tty->ops->shutdown(tty);
+	else
+		tty_shutdown(tty);
+
+	/* The hangup queue is now free so we can reuse it rather than
+	   waste a chunk of memory for each port */
+	INIT_WORK(&tty->hangup_work, release_one_tty);
+	schedule_work(&tty->hangup_work);
+}
+
+/**
+ *	tty_kref_put		-	release a tty kref
+ *	@tty: tty device
+ *
+ *	Release a reference to a tty device and if need be let the kref
+ *	layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+	if (tty)
+		kref_put(&tty->kref, queue_release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
+/**
+ *	release_tty		-	release tty structure memory
+ *
+ *	Release both @tty and a possible linked partner (think pty pair),
+ *	and decrement the refcount of the backing module.
+ *
+ *	Locking:
+ *		tty_mutex - sometimes only
+ *		takes the file list lock internally when working on the list
+ *	of ttys that the driver keeps.
+ *		FIXME: should we require tty_mutex is held here ??
+ *
+ */
+static void release_tty(struct tty_struct *tty, int idx)
+{
+	/* This should always be true but check for the moment */
+	WARN_ON(tty->index != idx);
+
+	if (tty->link)
+		tty_kref_put(tty->link);
+	tty_kref_put(tty);
+}
+
+/**
+ *	tty_release		-	vfs callback for close
+ *	@inode: inode of tty
+ *	@filp: file pointer for handle to tty
+ *
+ *	Called the last time each file handle is closed that references
+ *	this tty. There may however be several such references.
+ *
+ *	Locking:
+ *		Takes bkl. See tty_release_dev
+ *
+ * Even releasing the tty structures is a tricky business.. We have
+ * to be very careful that the structures are all released at the
+ * same time, as interrupts might otherwise get the wrong pointers.
+ *
+ * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
+ * lead to double frees or releasing memory still in use.
+ */
+
+int tty_release(struct inode *inode, struct file *filp)
+{
+	struct tty_struct *tty, *o_tty;
+	int	pty_master, tty_closing, o_tty_closing, do_sleep;
+	int	devpts;
+	int	idx;
+	char	buf[64];
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+		return 0;
+
+	lock_kernel();
+	check_tty_count(tty, "tty_release_dev");
+
+	tty_fasync(-1, filp, 0);
+
+	idx = tty->index;
+	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+		      tty->driver->subtype == PTY_TYPE_MASTER);
+	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
+	o_tty = tty->link;
+
+#ifdef TTY_PARANOIA_CHECK
+	if (idx < 0 || idx >= tty->driver->num) {
+		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
+				  "free (%s)\n", tty->name);
+		unlock_kernel();
+		return 0;
+	}
+	if (!devpts) {
+		if (tty != tty->driver->ttys[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
+			       "for (%s)\n", idx, tty->name);
+			return 0;
+		}
+		if (tty->termios != tty->driver->termios[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
+			       "for (%s)\n",
+			       idx, tty->name);
+			return 0;
+		}
+	}
+#endif
+
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
+	       tty_name(tty, buf), tty->count);
+#endif
+
+#ifdef TTY_PARANOIA_CHECK
+	if (tty->driver->other &&
+	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+		if (o_tty != tty->driver->other->ttys[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
+					  "not o_tty for (%s)\n",
+			       idx, tty->name);
+			return 0 ;
+		}
+		if (o_tty->termios != tty->driver->other->termios[idx]) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
+					  "not o_termios for (%s)\n",
+			       idx, tty->name);
+			return 0;
+		}
+		if (o_tty->link != tty) {
+			unlock_kernel();
+			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
+			return 0;
+		}
+	}
+#endif
+	if (tty->ops->close)
+		tty->ops->close(tty, filp);
+
+	unlock_kernel();
+	/*
+	 * Sanity check: if tty->count is going to zero, there shouldn't be
+	 * any waiters on tty->read_wait or tty->write_wait.  We test the
+	 * wait queues and kick everyone out _before_ actually starting to
+	 * close.  This ensures that we won't block while releasing the tty
+	 * structure.
+	 *
+	 * The test for the o_tty closing is necessary, since the master and
+	 * slave sides may close in any order.  If the slave side closes out
+	 * first, its count will be one, since the master side holds an open.
+	 * Thus this test wouldn't be triggered at the time the slave closes,
+	 * so we do it now.
+	 *
+	 * Note that it's possible for the tty to be opened again while we're
+	 * flushing out waiters.  By recalculating the closing flags before
+	 * each iteration we avoid any problems.
+	 */
+	while (1) {
+		/* Guard against races with tty->count changes elsewhere and
+		   opens on /dev/tty */
+
+		mutex_lock(&tty_mutex);
+		lock_kernel();
+		tty_closing = tty->count <= 1;
+		o_tty_closing = o_tty &&
+			(o_tty->count <= (pty_master ? 1 : 0));
+		do_sleep = 0;
+
+		if (tty_closing) {
+			if (waitqueue_active(&tty->read_wait)) {
+				wake_up_poll(&tty->read_wait, POLLIN);
+				do_sleep++;
+			}
+			if (waitqueue_active(&tty->write_wait)) {
+				wake_up_poll(&tty->write_wait, POLLOUT);
+				do_sleep++;
+			}
+		}
+		if (o_tty_closing) {
+			if (waitqueue_active(&o_tty->read_wait)) {
+				wake_up_poll(&o_tty->read_wait, POLLIN);
+				do_sleep++;
+			}
+			if (waitqueue_active(&o_tty->write_wait)) {
+				wake_up_poll(&o_tty->write_wait, POLLOUT);
+				do_sleep++;
+			}
+		}
+		if (!do_sleep)
+			break;
+
+		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
+				    "active!\n", tty_name(tty, buf));
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		schedule();
+	}
+
+	/*
+	 * The closing flags are now consistent with the open counts on
+	 * both sides, and we've completed the last operation that could
+	 * block, so it's safe to proceed with closing.
+	 */
+	if (pty_master) {
+		if (--o_tty->count < 0) {
+			printk(KERN_WARNING "tty_release_dev: bad pty slave count "
+					    "(%d) for %s\n",
+			       o_tty->count, tty_name(o_tty, buf));
+			o_tty->count = 0;
+		}
+	}
+	if (--tty->count < 0) {
+		printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
+		       tty->count, tty_name(tty, buf));
+		tty->count = 0;
+	}
+
+	/*
+	 * We've decremented tty->count, so we need to remove this file
+	 * descriptor off the tty->tty_files list; this serves two
+	 * purposes:
+	 *  - check_tty_count sees the correct number of file descriptors
+	 *    associated with this tty.
+	 *  - do_tty_hangup no longer sees this file descriptor as
+	 *    something that needs to be handled for hangups.
+	 */
+	file_kill(filp);
+	filp->private_data = NULL;
+
+	/*
+	 * Perform some housekeeping before deciding whether to return.
+	 *
+	 * Set the TTY_CLOSING flag if this was the last open.  In the
+	 * case of a pty we may have to wait around for the other side
+	 * to close, and TTY_CLOSING makes sure we can't be reopened.
+	 */
+	if (tty_closing)
+		set_bit(TTY_CLOSING, &tty->flags);
+	if (o_tty_closing)
+		set_bit(TTY_CLOSING, &o_tty->flags);
+
+	/*
+	 * If _either_ side is closing, make sure there aren't any
+	 * processes that still think tty or o_tty is their controlling
+	 * tty.
+	 */
+	if (tty_closing || o_tty_closing) {
+		read_lock(&tasklist_lock);
+		session_clear_tty(tty->session);
+		if (o_tty)
+			session_clear_tty(o_tty->session);
+		read_unlock(&tasklist_lock);
+	}
+
+	mutex_unlock(&tty_mutex);
+
+	/* check whether both sides are closing ... */
+	if (!tty_closing || (o_tty && !o_tty_closing)) {
+		unlock_kernel();
+		return 0;
+	}
+
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "freeing tty structure...");
+#endif
+	/*
+	 * Ask the line discipline code to release its structures
+	 */
+	tty_ldisc_release(tty, o_tty);
+	/*
+	 * The release_tty function takes care of the details of clearing
+	 * the slots and preserving the termios structure.
+	 */
+	release_tty(tty, idx);
+
+	/* Make this pty number available for reallocation */
+	if (devpts)
+		devpts_kill_index(inode, idx);
+	unlock_kernel();
+	return 0;
+}
+
+/**
+ *	tty_open		-	open a tty device
+ *	@inode: inode of device file
+ *	@filp: file pointer to tty
+ *
+ *	tty_open and tty_release keep up the tty count that contains the
+ *	number of opens done on a tty. We cannot use the inode-count, as
+ *	different inodes might point to the same tty.
+ *
+ *	Open-counting is needed for pty masters, as well as for keeping
+ *	track of serial lines: DTR is dropped when the last close happens.
+ *	(This is not done solely through tty->count, now.  - Ted 1/27/92)
+ *
+ *	The termios state of a pty is reset on first open so that
+ *	settings don't persist across reuse.
+ *
+ *	Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
+ *		 tty->count should protect the rest.
+ *		 ->siglock protects ->signal/->sighand
+ */
+
+static int tty_open(struct inode *inode, struct file *filp)
+{
+	struct tty_struct *tty = NULL;
+	int noctty, retval;
+	struct tty_driver *driver;
+	int index;
+	dev_t device = inode->i_rdev;
+	unsigned saved_flags = filp->f_flags;
+
+	nonseekable_open(inode, filp);
+
+retry_open:
+	noctty = filp->f_flags & O_NOCTTY;
+	index  = -1;
+	retval = 0;
+
+	mutex_lock(&tty_mutex);
+	lock_kernel();
+
+	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
+		tty = get_current_tty();
+		if (!tty) {
+			unlock_kernel();
+			mutex_unlock(&tty_mutex);
+			return -ENXIO;
+		}
+		driver = tty_driver_kref_get(tty->driver);
+		index = tty->index;
+		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
+		/* noctty = 1; */
+		/* FIXME: Should we take a driver reference ? */
+		tty_kref_put(tty);
+		goto got_driver;
+	}
+#ifdef CONFIG_VT
+	if (device == MKDEV(TTY_MAJOR, 0)) {
+		extern struct tty_driver *console_driver;
+		driver = tty_driver_kref_get(console_driver);
+		index = fg_console;
+		noctty = 1;
+		goto got_driver;
+	}
+#endif
+	if (device == MKDEV(TTYAUX_MAJOR, 1)) {
+		struct tty_driver *console_driver = console_device(&index);
+		if (console_driver) {
+			driver = tty_driver_kref_get(console_driver);
+			if (driver) {
+				/* Don't let /dev/console block */
+				filp->f_flags |= O_NONBLOCK;
+				noctty = 1;
+				goto got_driver;
+			}
+		}
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		return -ENODEV;
+	}
+
+	driver = get_tty_driver(device, &index);
+	if (!driver) {
+		unlock_kernel();
+		mutex_unlock(&tty_mutex);
+		return -ENODEV;
+	}
+got_driver:
+	if (!tty) {
+		/* check whether we're reopening an existing tty */
+		tty = tty_driver_lookup_tty(driver, inode, index);
+
+		if (IS_ERR(tty)) {
+			unlock_kernel();
+			mutex_unlock(&tty_mutex);
+			return PTR_ERR(tty);
+		}
+	}
+
+	if (tty) {
+		retval = tty_reopen(tty);
+		if (retval)
+			tty = ERR_PTR(retval);
+	} else
+		tty = tty_init_dev(driver, index, 0);
+
+	mutex_unlock(&tty_mutex);
+	tty_driver_kref_put(driver);
+	if (IS_ERR(tty)) {
+		unlock_kernel();
+		return PTR_ERR(tty);
+	}
+
+	filp->private_data = tty;
+	file_move(filp, &tty->tty_files);
+	check_tty_count(tty, "tty_open");
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+		noctty = 1;
+#ifdef TTY_DEBUG_HANGUP
+	printk(KERN_DEBUG "opening %s...", tty->name);
+#endif
+	if (!retval) {
+		if (tty->ops->open)
+			retval = tty->ops->open(tty, filp);
+		else
+			retval = -ENODEV;
+	}
+	filp->f_flags = saved_flags;
+
+	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
+						!capable(CAP_SYS_ADMIN))
+		retval = -EBUSY;
+
+	if (retval) {
+#ifdef TTY_DEBUG_HANGUP
+		printk(KERN_DEBUG "error %d in opening %s...", retval,
+		       tty->name);
+#endif
+		tty_release(inode, filp);
+		if (retval != -ERESTARTSYS) {
+			unlock_kernel();
+			return retval;
+		}
+		if (signal_pending(current)) {
+			unlock_kernel();
+			return retval;
+		}
+		schedule();
+		/*
+		 * Need to reset f_op in case a hangup happened.
+		 */
+		if (filp->f_op == &hung_up_tty_fops)
+			filp->f_op = &tty_fops;
+		unlock_kernel();
+		goto retry_open;
+	}
+	unlock_kernel();
+
+
+	mutex_lock(&tty_mutex);
+	lock_kernel();
+	spin_lock_irq(&current->sighand->siglock);
+	if (!noctty &&
+	    current->signal->leader &&
+	    !current->signal->tty &&
+	    tty->session == NULL)
+		__proc_set_tty(current, tty);
+	spin_unlock_irq(&current->sighand->siglock);
+	unlock_kernel();
+	mutex_unlock(&tty_mutex);
+	return 0;
+}
+
+
+
+/**
+ *	tty_poll	-	check tty status
+ *	@filp: file being polled
+ *	@wait: poll wait structures to update
+ *
+ *	Call the line discipline polling method to obtain the poll
+ *	status of the device.
+ *
+ *	Locking: locks called line discipline but ldisc poll method
+ *	may be re-entered freely by other callers.
+ */
+
+static unsigned int tty_poll(struct file *filp, poll_table *wait)
+{
+	struct tty_struct *tty;
+	struct tty_ldisc *ld;
+	int ret = 0;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
+		return 0;
+
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->poll)
+		ret = (ld->ops->poll)(tty, filp, wait);
+	tty_ldisc_deref(ld);
+	return ret;
+}
+
+static int tty_fasync(int fd, struct file *filp, int on)
+{
+	struct tty_struct *tty;
+	unsigned long flags;
+	int retval = 0;
+
+	lock_kernel();
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
+		goto out;
+
+	retval = fasync_helper(fd, filp, on, &tty->fasync);
+	if (retval <= 0)
+		goto out;
+
+	if (on) {
+		enum pid_type type;
+		struct pid *pid;
+		if (!waitqueue_active(&tty->read_wait))
+			tty->minimum_to_wake = 1;
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		if (tty->pgrp) {
+			pid = tty->pgrp;
+			type = PIDTYPE_PGID;
+		} else {
+			pid = task_pid(current);
+			type = PIDTYPE_PID;
+		}
+		get_pid(pid);
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		retval = __f_setown(filp, pid, type, 0);
+		put_pid(pid);
+		if (retval)
+			goto out;
+	} else {
+		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
+			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+	}
+	retval = 0;
+out:
+	unlock_kernel();
+	return retval;
+}
+
+/**
+ *	tiocsti			-	fake input character
+ *	@tty: tty to fake input into
+ *	@p: pointer to character
+ *
+ *	Fake input to a tty device. Does the necessary locking and
+ *	input management.
+ *
+ *	FIXME: does not honour flow control ??
+ *
+ *	Locking:
+ *		Called functions take tty_ldisc_lock
+ *		current->signal->tty check is safe without locks
+ *
+ *	FIXME: may race normal receive processing
+ */
+
+static int tiocsti(struct tty_struct *tty, char __user *p)
+{
+	char ch, mbz = 0;
+	struct tty_ldisc *ld;
+
+	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (get_user(ch, p))
+		return -EFAULT;
+	tty_audit_tiocsti(tty, ch);
+	ld = tty_ldisc_ref_wait(tty);
+	ld->ops->receive_buf(tty, &ch, &mbz, 1);
+	tty_ldisc_deref(ld);
+	return 0;
+}
+
+/**
+ *	tiocgwinsz		-	implement window query ioctl
+ *	@tty; tty
+ *	@arg: user buffer for result
+ *
+ *	Copies the kernel idea of the window size into the user buffer.
+ *
+ *	Locking: tty->termios_mutex is taken to ensure the winsize data
+ *		is consistent.
+ */
+
+static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
+{
+	int err;
+
+	mutex_lock(&tty->termios_mutex);
+	err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
+	mutex_unlock(&tty->termios_mutex);
+
+	return err ? -EFAULT: 0;
+}
+
+/**
+ *	tty_do_resize		-	resize event
+ *	@tty: tty being resized
+ *	@rows: rows (character)
+ *	@cols: cols (character)
+ *
+ *	Update the termios variables and send the necessary signals to
+ *	peform a terminal resize correctly
+ */
+
+int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
+{
+	struct pid *pgrp;
+	unsigned long flags;
+
+	/* Lock the tty */
+	mutex_lock(&tty->termios_mutex);
+	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
+		goto done;
+	/* Get the PID values and reference them so we can
+	   avoid holding the tty ctrl lock while sending signals */
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	pgrp = get_pid(tty->pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	if (pgrp)
+		kill_pgrp(pgrp, SIGWINCH, 1);
+	put_pid(pgrp);
+
+	tty->winsize = *ws;
+done:
+	mutex_unlock(&tty->termios_mutex);
+	return 0;
+}
+
+/**
+ *	tiocswinsz		-	implement window size set ioctl
+ *	@tty; tty side of tty
+ *	@arg: user buffer for result
+ *
+ *	Copies the user idea of the window size to the kernel. Traditionally
+ *	this is just advisory information but for the Linux console it
+ *	actually has driver level meaning and triggers a VC resize.
+ *
+ *	Locking:
+ *		Driver dependant. The default do_resize method takes the
+ *	tty termios mutex and ctrl_lock. The console takes its own lock
+ *	then calls into the default method.
+ */
+
+static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
+{
+	struct winsize tmp_ws;
+	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
+		return -EFAULT;
+
+	if (tty->ops->resize)
+		return tty->ops->resize(tty, &tmp_ws);
+	else
+		return tty_do_resize(tty, &tmp_ws);
+}
+
+/**
+ *	tioccons	-	allow admin to move logical console
+ *	@file: the file to become console
+ *
+ *	Allow the adminstrator to move the redirected console device
+ *
+ *	Locking: uses redirect_lock to guard the redirect information
+ */
+
+static int tioccons(struct file *file)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (file->f_op->write == redirected_tty_write) {
+		struct file *f;
+		spin_lock(&redirect_lock);
+		f = redirect;
+		redirect = NULL;
+		spin_unlock(&redirect_lock);
+		if (f)
+			fput(f);
+		return 0;
+	}
+	spin_lock(&redirect_lock);
+	if (redirect) {
+		spin_unlock(&redirect_lock);
+		return -EBUSY;
+	}
+	get_file(file);
+	redirect = file;
+	spin_unlock(&redirect_lock);
+	return 0;
+}
+
+/**
+ *	fionbio		-	non blocking ioctl
+ *	@file: file to set blocking value
+ *	@p: user parameter
+ *
+ *	Historical tty interfaces had a blocking control ioctl before
+ *	the generic functionality existed. This piece of history is preserved
+ *	in the expected tty API of posix OS's.
+ *
+ *	Locking: none, the open file handle ensures it won't go away.
+ */
+
+static int fionbio(struct file *file, int __user *p)
+{
+	int nonblock;
+
+	if (get_user(nonblock, p))
+		return -EFAULT;
+
+	spin_lock(&file->f_lock);
+	if (nonblock)
+		file->f_flags |= O_NONBLOCK;
+	else
+		file->f_flags &= ~O_NONBLOCK;
+	spin_unlock(&file->f_lock);
+	return 0;
+}
+
+/**
+ *	tiocsctty	-	set controlling tty
+ *	@tty: tty structure
+ *	@arg: user argument
+ *
+ *	This ioctl is used to manage job control. It permits a session
+ *	leader to set this tty as the controlling tty for the session.
+ *
+ *	Locking:
+ *		Takes tty_mutex() to protect tty instance
+ *		Takes tasklist_lock internally to walk sessions
+ *		Takes ->siglock() when updating signal->tty
+ */
+
+static int tiocsctty(struct tty_struct *tty, int arg)
+{
+	int ret = 0;
+	if (current->signal->leader && (task_session(current) == tty->session))
+		return ret;
+
+	mutex_lock(&tty_mutex);
+	/*
+	 * The process must be a session leader and
+	 * not have a controlling tty already.
+	 */
+	if (!current->signal->leader || current->signal->tty) {
+		ret = -EPERM;
+		goto unlock;
+	}
+
+	if (tty->session) {
+		/*
+		 * This tty is already the controlling
+		 * tty for another session group!
+		 */
+		if (arg == 1 && capable(CAP_SYS_ADMIN)) {
+			/*
+			 * Steal it away
+			 */
+			read_lock(&tasklist_lock);
+			session_clear_tty(tty->session);
+			read_unlock(&tasklist_lock);
+		} else {
+			ret = -EPERM;
+			goto unlock;
+		}
+	}
+	proc_set_tty(current, tty);
+unlock:
+	mutex_unlock(&tty_mutex);
+	return ret;
+}
+
+/**
+ *	tty_get_pgrp	-	return a ref counted pgrp pid
+ *	@tty: tty to read
+ *
+ *	Returns a refcounted instance of the pid struct for the process
+ *	group controlling the tty.
+ */
+
+struct pid *tty_get_pgrp(struct tty_struct *tty)
+{
+	unsigned long flags;
+	struct pid *pgrp;
+
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	pgrp = get_pid(tty->pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+	return pgrp;
+}
+EXPORT_SYMBOL_GPL(tty_get_pgrp);
+
+/**
+ *	tiocgpgrp		-	get process group
+ *	@tty: tty passed by user
+ *	@real_tty: tty side of the tty pased by the user if a pty else the tty
+ *	@p: returned pid
+ *
+ *	Obtain the process group of the tty. If there is no process group
+ *	return an error.
+ *
+ *	Locking: none. Reference to current->signal->tty is safe.
+ */
+
+static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	struct pid *pid;
+	int ret;
+	/*
+	 * (tty == real_tty) is a cheap way of
+	 * testing if the tty is NOT a master pty.
+	 */
+	if (tty == real_tty && current->signal->tty != real_tty)
+		return -ENOTTY;
+	pid = tty_get_pgrp(real_tty);
+	ret =  put_user(pid_vnr(pid), p);
+	put_pid(pid);
+	return ret;
+}
+
+/**
+ *	tiocspgrp		-	attempt to set process group
+ *	@tty: tty passed by user
+ *	@real_tty: tty side device matching tty passed by user
+ *	@p: pid pointer
+ *
+ *	Set the process group of the tty to the session passed. Only
+ *	permitted where the tty session is our session.
+ *
+ *	Locking: RCU, ctrl lock
+ */
+
+static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	struct pid *pgrp;
+	pid_t pgrp_nr;
+	int retval = tty_check_change(real_tty);
+	unsigned long flags;
+
+	if (retval == -EIO)
+		return -ENOTTY;
+	if (retval)
+		return retval;
+	if (!current->signal->tty ||
+	    (current->signal->tty != real_tty) ||
+	    (real_tty->session != task_session(current)))
+		return -ENOTTY;
+	if (get_user(pgrp_nr, p))
+		return -EFAULT;
+	if (pgrp_nr < 0)
+		return -EINVAL;
+	rcu_read_lock();
+	pgrp = find_vpid(pgrp_nr);
+	retval = -ESRCH;
+	if (!pgrp)
+		goto out_unlock;
+	retval = -EPERM;
+	if (session_of_pgrp(pgrp) != task_session(current))
+		goto out_unlock;
+	retval = 0;
+	spin_lock_irqsave(&tty->ctrl_lock, flags);
+	put_pid(real_tty->pgrp);
+	real_tty->pgrp = get_pid(pgrp);
+	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+out_unlock:
+	rcu_read_unlock();
+	return retval;
+}
+
+/**
+ *	tiocgsid		-	get session id
+ *	@tty: tty passed by user
+ *	@real_tty: tty side of the tty pased by the user if a pty else the tty
+ *	@p: pointer to returned session id
+ *
+ *	Obtain the session id of the tty. If there is no session
+ *	return an error.
+ *
+ *	Locking: none. Reference to current->signal->tty is safe.
+ */
+
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
+{
+	/*
+	 * (tty == real_tty) is a cheap way of
+	 * testing if the tty is NOT a master pty.
+	*/
+	if (tty == real_tty && current->signal->tty != real_tty)
+		return -ENOTTY;
+	if (!real_tty->session)
+		return -ENOTTY;
+	return put_user(pid_vnr(real_tty->session), p);
+}
+
+/**
+ *	tiocsetd	-	set line discipline
+ *	@tty: tty device
+ *	@p: pointer to user data
+ *
+ *	Set the line discipline according to user request.
+ *
+ *	Locking: see tty_set_ldisc, this function is just a helper
+ */
+
+static int tiocsetd(struct tty_struct *tty, int __user *p)
+{
+	int ldisc;
+	int ret;
+
+	if (get_user(ldisc, p))
+		return -EFAULT;
+
+	ret = tty_set_ldisc(tty, ldisc);
+
+	return ret;
+}
+
+/**
+ *	send_break	-	performed time break
+ *	@tty: device to break on
+ *	@duration: timeout in mS
+ *
+ *	Perform a timed break on hardware that lacks its own driver level
+ *	timed break functionality.
+ *
+ *	Locking:
+ *		atomic_write_lock serializes
+ *
+ */
+
+static int send_break(struct tty_struct *tty, unsigned int duration)
+{
+	int retval;
+
+	if (tty->ops->break_ctl == NULL)
+		return 0;
+
+	if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
+		retval = tty->ops->break_ctl(tty, duration);
+	else {
+		/* Do the work ourselves */
+		if (tty_write_lock(tty, 0) < 0)
+			return -EINTR;
+		retval = tty->ops->break_ctl(tty, -1);
+		if (retval)
+			goto out;
+		if (!signal_pending(current))
+			msleep_interruptible(duration);
+		retval = tty->ops->break_ctl(tty, 0);
+out:
+		tty_write_unlock(tty);
+		if (signal_pending(current))
+			retval = -EINTR;
+	}
+	return retval;
+}
+
+/**
+ *	tty_tiocmget		-	get modem status
+ *	@tty: tty device
+ *	@file: user file pointer
+ *	@p: pointer to result
+ *
+ *	Obtain the modem status bits from the tty driver if the feature
+ *	is supported. Return -EINVAL if it is not available.
+ *
+ *	Locking: none (up to the driver)
+ */
+
+static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
+{
+	int retval = -EINVAL;
+
+	if (tty->ops->tiocmget) {
+		retval = tty->ops->tiocmget(tty, file);
+
+		if (retval >= 0)
+			retval = put_user(retval, p);
+	}
+	return retval;
+}
+
+/**
+ *	tty_tiocmset		-	set modem status
+ *	@tty: tty device
+ *	@file: user file pointer
+ *	@cmd: command - clear bits, set bits or set all
+ *	@p: pointer to desired bits
+ *
+ *	Set the modem status bits from the tty driver if the feature
+ *	is supported. Return -EINVAL if it is not available.
+ *
+ *	Locking: none (up to the driver)
+ */
+
+static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
+	     unsigned __user *p)
+{
+	int retval;
+	unsigned int set, clear, val;
+
+	if (tty->ops->tiocmset == NULL)
+		return -EINVAL;
+
+	retval = get_user(val, p);
+	if (retval)
+		return retval;
+	set = clear = 0;
+	switch (cmd) {
+	case TIOCMBIS:
+		set = val;
+		break;
+	case TIOCMBIC:
+		clear = val;
+		break;
+	case TIOCMSET:
+		set = val;
+		clear = ~val;
+		break;
+	}
+	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
+	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP|TIOCM_CD|
+		TIOCM_RI|TIOCM_DSR|TIOCM_CTS;
+	return tty->ops->tiocmset(tty, file, set, clear);
+}
+
+struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+		tty = tty->link;
+	return tty;
+}
+EXPORT_SYMBOL(tty_pair_get_tty);
+
+struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+{
+	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver->subtype == PTY_TYPE_MASTER)
+	    return tty;
+	return tty->link;
+}
+EXPORT_SYMBOL(tty_pair_get_pty);
+
+/*
+ * Split this up, as gcc can choke on it otherwise..
+ */
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct tty_struct *tty, *real_tty;
+	void __user *p = (void __user *)arg;
+	int retval;
+	struct tty_ldisc *ld;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+		return -EINVAL;
+
+	real_tty = tty_pair_get_tty(tty);
+
+	/*
+	 * Factor out some common prep work
+	 */
+	switch (cmd) {
+	case TIOCSETD:
+	case TIOCSBRK:
+	case TIOCCBRK:
+	case TCSBRK:
+	case TCSBRKP:
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		if (cmd != TIOCCBRK) {
+			tty_wait_until_sent(tty, 0);
+			if (signal_pending(current))
+				return -EINTR;
+		}
+		break;
+	}
+
+	/*
+	 *	Now do the stuff.
+	 */
+	switch (cmd) {
+	case TIOCSTI:
+		return tiocsti(tty, p);
+	case TIOCGWINSZ:
+		return tiocgwinsz(real_tty, p);
+	case TIOCSWINSZ:
+		return tiocswinsz(real_tty, p);
+	case TIOCCONS:
+		return real_tty != tty ? -EINVAL : tioccons(file);
+	case FIONBIO:
+		return fionbio(file, p);
+	case TIOCEXCL:
+		set_bit(TTY_EXCLUSIVE, &tty->flags);
+		return 0;
+	case TIOCNXCL:
+		clear_bit(TTY_EXCLUSIVE, &tty->flags);
+		return 0;
+	case TIOCNOTTY:
+		if (current->signal->tty != tty)
+			return -ENOTTY;
+		no_tty();
+		return 0;
+	case TIOCSCTTY:
+		return tiocsctty(tty, arg);
+	case TIOCGPGRP:
+		return tiocgpgrp(tty, real_tty, p);
+	case TIOCSPGRP:
+		return tiocspgrp(tty, real_tty, p);
+	case TIOCGSID:
+		return tiocgsid(tty, real_tty, p);
+	case TIOCGETD:
+		return put_user(tty->ldisc->ops->num, (int __user *)p);
+	case TIOCSETD:
+		return tiocsetd(tty, p);
+	/*
+	 * Break handling
+	 */
+	case TIOCSBRK:	/* Turn break on, unconditionally */
+		if (tty->ops->break_ctl)
+			return tty->ops->break_ctl(tty, -1);
+		return 0;
+	case TIOCCBRK:	/* Turn break off, unconditionally */
+		if (tty->ops->break_ctl)
+			return tty->ops->break_ctl(tty, 0);
+		return 0;
+	case TCSBRK:   /* SVID version: non-zero arg --> no break */
+		/* non-zero arg means wait for all output data
+		 * to be sent (performed above) but don't send break.
+		 * This is used by the tcdrain() termios function.
+		 */
+		if (!arg)
+			return send_break(tty, 250);
+		return 0;
+	case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		return send_break(tty, arg ? arg*100 : 250);
+
+	case TIOCMGET:
+		return tty_tiocmget(tty, file, p);
+	case TIOCMSET:
+	case TIOCMBIC:
+	case TIOCMBIS:
+		return tty_tiocmset(tty, file, cmd, p);
+	case TCFLSH:
+		switch (arg) {
+		case TCIFLUSH:
+		case TCIOFLUSH:
+		/* flush tty buffer and allow ldisc to process ioctl */
+			tty_buffer_flush(tty);
+			break;
+		}
+		break;
+	}
+	if (tty->ops->ioctl) {
+		retval = (tty->ops->ioctl)(tty, file, cmd, arg);
+		if (retval != -ENOIOCTLCMD)
+			return retval;
+	}
+	ld = tty_ldisc_ref_wait(tty);
+	retval = -EINVAL;
+	if (ld->ops->ioctl) {
+		retval = ld->ops->ioctl(tty, file, cmd, arg);
+		if (retval == -ENOIOCTLCMD)
+			retval = -EINVAL;
+	}
+	tty_ldisc_deref(ld);
+	return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long tty_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct tty_struct *tty = file->private_data;
+	struct tty_ldisc *ld;
+	int retval = -ENOIOCTLCMD;
+
+	if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+		return -EINVAL;
+
+	if (tty->ops->compat_ioctl) {
+		retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg);
+		if (retval != -ENOIOCTLCMD)
+			return retval;
+	}
+
+	ld = tty_ldisc_ref_wait(tty);
+	if (ld->ops->compat_ioctl)
+		retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
+	tty_ldisc_deref(ld);
+
+	return retval;
+}
+#endif
+
+/*
+ * This implements the "Secure Attention Key" ---  the idea is to
+ * prevent trojan horses by killing all processes associated with this
+ * tty when the user hits the "Secure Attention Key".  Required for
+ * super-paranoid applications --- see the Orange Book for more details.
+ *
+ * This code could be nicer; ideally it should send a HUP, wait a few
+ * seconds, then send a INT, and then a KILL signal.  But you then
+ * have to coordinate with the init process, since all processes associated
+ * with the current tty must be dead before the new getty is allowed
+ * to spawn.
+ *
+ * Now, if it would be correct ;-/ The current code has a nasty hole -
+ * it doesn't catch files in flight. We may send the descriptor to ourselves
+ * via AF_UNIX socket, close it and later fetch from socket. FIXME.
+ *
+ * Nasty bug: do_SAK is being called in interrupt context.  This can
+ * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
+ */
+void __do_SAK(struct tty_struct *tty)
+{
+#ifdef TTY_SOFT_SAK
+	tty_hangup(tty);
+#else
+	struct task_struct *g, *p;
+	struct pid *session;
+	int		i;
+	struct file	*filp;
+	struct fdtable *fdt;
+
+	if (!tty)
+		return;
+	session = tty->session;
+
+	tty_ldisc_flush(tty);
+
+	tty_driver_flush_buffer(tty);
+
+	read_lock(&tasklist_lock);
+	/* Kill the entire session */
+	do_each_pid_task(session, PIDTYPE_SID, p) {
+		printk(KERN_NOTICE "SAK: killed process %d"
+			" (%s): task_session(p)==tty->session\n",
+			task_pid_nr(p), p->comm);
+		send_sig(SIGKILL, p, 1);
+	} while_each_pid_task(session, PIDTYPE_SID, p);
+	/* Now kill any processes that happen to have the
+	 * tty open.
+	 */
+	do_each_thread(g, p) {
+		if (p->signal->tty == tty) {
+			printk(KERN_NOTICE "SAK: killed process %d"
+			    " (%s): task_session(p)==tty->session\n",
+			    task_pid_nr(p), p->comm);
+			send_sig(SIGKILL, p, 1);
+			continue;
+		}
+		task_lock(p);
+		if (p->files) {
+			/*
+			 * We don't take a ref to the file, so we must
+			 * hold ->file_lock instead.
+			 */
+			spin_lock(&p->files->file_lock);
+			fdt = files_fdtable(p->files);
+			for (i = 0; i < fdt->max_fds; i++) {
+				filp = fcheck_files(p->files, i);
+				if (!filp)
+					continue;
+				if (filp->f_op->read == tty_read &&
+				    filp->private_data == tty) {
+					printk(KERN_NOTICE "SAK: killed process %d"
+					    " (%s): fd#%d opened to the tty\n",
+					    task_pid_nr(p), p->comm, i);
+					force_sig(SIGKILL, p);
+					break;
+				}
+			}
+			spin_unlock(&p->files->file_lock);
+		}
+		task_unlock(p);
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+#endif
+}
+
+static void do_SAK_work(struct work_struct *work)
+{
+	struct tty_struct *tty =
+		container_of(work, struct tty_struct, SAK_work);
+	__do_SAK(tty);
+}
+
+/*
+ * The tq handling here is a little racy - tty->SAK_work may already be queued.
+ * Fortunately we don't need to worry, because if ->SAK_work is already queued,
+ * the values which we write to it will be identical to the values which it
+ * already has. --akpm
+ */
+void do_SAK(struct tty_struct *tty)
+{
+	if (!tty)
+		return;
+	schedule_work(&tty->SAK_work);
+}
+
+EXPORT_SYMBOL(do_SAK);
+
+/**
+ *	initialize_tty_struct
+ *	@tty: tty to initialize
+ *
+ *	This subroutine initializes a tty structure that has been newly
+ *	allocated.
+ *
+ *	Locking: none - tty in question must not be exposed at this point
+ */
+
+void initialize_tty_struct(struct tty_struct *tty,
+		struct tty_driver *driver, int idx)
+{
+	memset(tty, 0, sizeof(struct tty_struct));
+	kref_init(&tty->kref);
+	tty->magic = TTY_MAGIC;
+	tty_ldisc_init(tty);
+	tty->session = NULL;
+	tty->pgrp = NULL;
+	tty->overrun_time = jiffies;
+	tty->buf.head = tty->buf.tail = NULL;
+	tty_buffer_init(tty);
+	mutex_init(&tty->termios_mutex);
+	mutex_init(&tty->ldisc_mutex);
+	init_waitqueue_head(&tty->write_wait);
+	init_waitqueue_head(&tty->read_wait);
+	INIT_WORK(&tty->hangup_work, do_tty_hangup);
+	mutex_init(&tty->atomic_read_lock);
+	mutex_init(&tty->atomic_write_lock);
+	mutex_init(&tty->output_lock);
+	mutex_init(&tty->echo_lock);
+	spin_lock_init(&tty->read_lock);
+	spin_lock_init(&tty->ctrl_lock);
+	INIT_LIST_HEAD(&tty->tty_files);
+	INIT_WORK(&tty->SAK_work, do_SAK_work);
+
+	tty->driver = driver;
+	tty->ops = driver->ops;
+	tty->index = idx;
+	tty_line_name(driver, idx, tty->name);
+}
+
+/**
+ *	tty_put_char	-	write one character to a tty
+ *	@tty: tty
+ *	@ch: character
+ *
+ *	Write one byte to the tty using the provided put_char method
+ *	if present. Returns the number of characters successfully output.
+ *
+ *	Note: the specific put_char operation in the driver layer may go
+ *	away soon. Don't call it directly, use this method
+ */
+
+int tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	if (tty->ops->put_char)
+		return tty->ops->put_char(tty, ch);
+	return tty->ops->write(tty, &ch, 1);
+}
+EXPORT_SYMBOL_GPL(tty_put_char);
+
+struct class *tty_class;
+
+/**
+ *	tty_register_device - register a tty device
+ *	@driver: the tty driver that describes the tty device
+ *	@index: the index in the tty driver for this tty device
+ *	@device: a struct device that is associated with this tty device.
+ *		This field is optional, if there is no known struct device
+ *		for this tty device it can be set to NULL safely.
+ *
+ *	Returns a pointer to the struct device for this tty device
+ *	(or ERR_PTR(-EFOO) on error).
+ *
+ *	This call is required to be made to register an individual tty device
+ *	if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
+ *	that bit is not set, this function should not be called by a tty
+ *	driver.
+ *
+ *	Locking: ??
+ */
+
+struct device *tty_register_device(struct tty_driver *driver, unsigned index,
+				   struct device *device)
+{
+	char name[64];
+	dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
+
+	if (index >= driver->num) {
+		printk(KERN_ERR "Attempt to register invalid tty line number "
+		       " (%d).\n", index);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (driver->type == TTY_DRIVER_TYPE_PTY)
+		pty_line_name(driver, index, name);
+	else
+		tty_line_name(driver, index, name);
+
+	return device_create(tty_class, device, dev, NULL, name);
+}
+EXPORT_SYMBOL(tty_register_device);
+
+/**
+ * 	tty_unregister_device - unregister a tty device
+ * 	@driver: the tty driver that describes the tty device
+ * 	@index: the index in the tty driver for this tty device
+ *
+ * 	If a tty device is registered with a call to tty_register_device() then
+ *	this function must be called when the tty device is gone.
+ *
+ *	Locking: ??
+ */
+
+void tty_unregister_device(struct tty_driver *driver, unsigned index)
+{
+	device_destroy(tty_class,
+		MKDEV(driver->major, driver->minor_start) + index);
+}
+EXPORT_SYMBOL(tty_unregister_device);
+
+struct tty_driver *alloc_tty_driver(int lines)
+{
+	struct tty_driver *driver;
+
+	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
+	if (driver) {
+		kref_init(&driver->kref);
+		driver->magic = TTY_DRIVER_MAGIC;
+		driver->num = lines;
+		/* later we'll move allocation of tables here */
+	}
+	return driver;
+}
+EXPORT_SYMBOL(alloc_tty_driver);
+
+static void destruct_tty_driver(struct kref *kref)
+{
+	struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
+	int i;
+	struct ktermios *tp;
+	void *p;
+
+	if (driver->flags & TTY_DRIVER_INSTALLED) {
+		/*
+		 * Free the termios and termios_locked structures because
+		 * we don't want to get memory leaks when modular tty
+		 * drivers are removed from the kernel.
+		 */
+		for (i = 0; i < driver->num; i++) {
+			tp = driver->termios[i];
+			if (tp) {
+				driver->termios[i] = NULL;
+				kfree(tp);
+			}
+			if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
+				tty_unregister_device(driver, i);
+		}
+		p = driver->ttys;
+		proc_tty_unregister_driver(driver);
+		driver->ttys = NULL;
+		driver->termios = NULL;
+		kfree(p);
+		cdev_del(&driver->cdev);
+	}
+	kfree(driver);
+}
+
+void tty_driver_kref_put(struct tty_driver *driver)
+{
+	kref_put(&driver->kref, destruct_tty_driver);
+}
+EXPORT_SYMBOL(tty_driver_kref_put);
+
+void tty_set_operations(struct tty_driver *driver,
+			const struct tty_operations *op)
+{
+	driver->ops = op;
+};
+EXPORT_SYMBOL(tty_set_operations);
+
+void put_tty_driver(struct tty_driver *d)
+{
+	tty_driver_kref_put(d);
+}
+EXPORT_SYMBOL(put_tty_driver);
+
+/*
+ * Called by a tty driver to register itself.
+ */
+int tty_register_driver(struct tty_driver *driver)
+{
+	int error;
+	int i;
+	dev_t dev;
+	void **p = NULL;
+
+	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
+		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	if (!driver->major) {
+		error = alloc_chrdev_region(&dev, driver->minor_start,
+						driver->num, driver->name);
+		if (!error) {
+			driver->major = MAJOR(dev);
+			driver->minor_start = MINOR(dev);
+		}
+	} else {
+		dev = MKDEV(driver->major, driver->minor_start);
+		error = register_chrdev_region(dev, driver->num, driver->name);
+	}
+	if (error < 0) {
+		kfree(p);
+		return error;
+	}
+
+	if (p) {
+		driver->ttys = (struct tty_struct **)p;
+		driver->termios = (struct ktermios **)(p + driver->num);
+	} else {
+		driver->ttys = NULL;
+		driver->termios = NULL;
+	}
+
+	cdev_init(&driver->cdev, &tty_fops);
+	driver->cdev.owner = driver->owner;
+	error = cdev_add(&driver->cdev, dev, driver->num);
+	if (error) {
+		unregister_chrdev_region(dev, driver->num);
+		driver->ttys = NULL;
+		driver->termios = NULL;
+		kfree(p);
+		return error;
+	}
+
+	mutex_lock(&tty_mutex);
+	list_add(&driver->tty_drivers, &tty_drivers);
+	mutex_unlock(&tty_mutex);
+
+	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
+		for (i = 0; i < driver->num; i++)
+		    tty_register_device(driver, i, NULL);
+	}
+	proc_tty_register_driver(driver);
+	driver->flags |= TTY_DRIVER_INSTALLED;
+	return 0;
+}
+
+EXPORT_SYMBOL(tty_register_driver);
+
+/*
+ * Called by a tty driver to unregister itself.
+ */
+int tty_unregister_driver(struct tty_driver *driver)
+{
+#if 0
+	/* FIXME */
+	if (driver->refcount)
+		return -EBUSY;
+#endif
+	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
+				driver->num);
+	mutex_lock(&tty_mutex);
+	list_del(&driver->tty_drivers);
+	mutex_unlock(&tty_mutex);
+	return 0;
+}
+
+EXPORT_SYMBOL(tty_unregister_driver);
+
+dev_t tty_devnum(struct tty_struct *tty)
+{
+	return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
+}
+EXPORT_SYMBOL(tty_devnum);
+
+void proc_clear_tty(struct task_struct *p)
+{
+	unsigned long flags;
+	struct tty_struct *tty;
+	spin_lock_irqsave(&p->sighand->siglock, flags);
+	tty = p->signal->tty;
+	p->signal->tty = NULL;
+	spin_unlock_irqrestore(&p->sighand->siglock, flags);
+	tty_kref_put(tty);
+}
+
+/* Called under the sighand lock */
+
+static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+	if (tty) {
+		unsigned long flags;
+		/* We should not have a session or pgrp to put here but.... */
+		spin_lock_irqsave(&tty->ctrl_lock, flags);
+		put_pid(tty->session);
+		put_pid(tty->pgrp);
+		tty->pgrp = get_pid(task_pgrp(tsk));
+		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+		tty->session = get_pid(task_session(tsk));
+		if (tsk->signal->tty) {
+			printk(KERN_DEBUG "tty not NULL!!\n");
+			tty_kref_put(tsk->signal->tty);
+		}
+	}
+	put_pid(tsk->signal->tty_old_pgrp);
+	tsk->signal->tty = tty_kref_get(tty);
+	tsk->signal->tty_old_pgrp = NULL;
+}
+
+static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
+{
+	spin_lock_irq(&tsk->sighand->siglock);
+	__proc_set_tty(tsk, tty);
+	spin_unlock_irq(&tsk->sighand->siglock);
+}
+
+struct tty_struct *get_current_tty(void)
+{
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&current->sighand->siglock, flags);
+	tty = tty_kref_get(current->signal->tty);
+	spin_unlock_irqrestore(&current->sighand->siglock, flags);
+	return tty;
+}
+EXPORT_SYMBOL_GPL(get_current_tty);
+
+void tty_default_fops(struct file_operations *fops)
+{
+	*fops = tty_fops;
+}
+
+/*
+ * Initialize the console device. This is called *early*, so
+ * we can't necessarily depend on lots of kernel help here.
+ * Just do some early initializations, and do the complex setup
+ * later.
+ */
+void __init console_init(void)
+{
+	initcall_t *call;
+
+	/* Setup the default TTY line discipline. */
+	tty_ldisc_begin();
+
+	/*
+	 * set up the console device so that later boot sequences can
+	 * inform about problems etc..
+	 */
+	call = __con_initcall_start;
+	while (call < __con_initcall_end) {
+		(*call)();
+		call++;
+	}
+}
+
+static char *tty_devnode(struct device *dev, mode_t *mode)
+{
+	if (!mode)
+		return NULL;
+	if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
+	    dev->devt == MKDEV(TTYAUX_MAJOR, 2))
+		*mode = 0666;
+	return NULL;
+}
+
+static int __init tty_class_init(void)
+{
+	tty_class = class_create(THIS_MODULE, "tty");
+	if (IS_ERR(tty_class))
+		return PTR_ERR(tty_class);
+	tty_class->devnode = tty_devnode;
+	return 0;
+}
+
+postcore_initcall(tty_class_init);
+
+/* 3/2004 jmc: why do these devices exist? */
+
+static struct cdev tty_cdev, console_cdev;
+
+/*
+ * Ok, now we can initialize the rest of the tty devices and can count
+ * on memory allocations, interrupts etc..
+ */
+int __init tty_init(void)
+{
+	cdev_init(&tty_cdev, &tty_fops);
+	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
+		panic("Couldn't register /dev/tty driver\n");
+	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+			      "tty");
+
+	cdev_init(&console_cdev, &console_fops);
+	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
+	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
+		panic("Couldn't register /dev/console driver\n");
+	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+			      "console");
+
+#ifdef CONFIG_VT
+	vty_init(&console_fops);
+#endif
+	return 0;
+}
+