linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH][2/3][RFC] TDM Framework
@ 2012-03-10 12:57 Poonam Aggrwal
  2012-04-23  8:00 ` Aggrwal Poonam-B10812
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Poonam Aggrwal @ 2012-03-10 12:57 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Sandeep Singh, Poonam Aggrwal

From: Sandeep Singh <Sandeep@freescale.com>

TDM Framework is an attempt to provide a platform independent layer which
can offer a standard interface  for TDM access to different client modules.
Beneath, the framework layer can house different types of TDM drivers to handle
various TDM devices, the hardware intricacies of the devices being completely
taken care by TDM drivers.

This framework layer will allow any type of TDM device to hook with it.
For example Freescale controller as on MPC8315, UCC based TDM controller, or 
any other controller.

The main functions of this Framework are:
- provides interface to TDM clients to access TDM functionalities.
- provides standard interface for TDM drivers to hook with the framework. 
- handles various data handling stuff and buffer management.

In future this Framework will be extended to provide Interface for Line control
devices also. For example SLIC, E1/T1 Framers etc.

Limitations/Future Work
---------------------------
1. Presently the framework supports only Single Port channelised mode.
2. Also the configurability options are limited which will be extended later on.
3. Only kernel mode TDM clients are supported currently. Support for User mode
clients will be added later. 

Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
---
 A couple of todos' are left in the patch, we are working on it and will be
addressed in the updated patch set.
 drivers/Kconfig                 |    1 +
 drivers/Makefile                |    1 +
 drivers/tdm/Kconfig             |   25 +
 drivers/tdm/tdm-core.c          | 1146 +++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |   11 +
 include/linux/tdm.h             |  347 ++++++++++++
 6 files changed, 1531 insertions(+), 0 deletions(-)
 create mode 100644 drivers/tdm/Kconfig
 create mode 100644 drivers/tdm/tdm-core.c
 create mode 100644 include/linux/tdm.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index ad6c1eb..25f7f5b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
 
 source "drivers/net/dpa/NetCommSw/Kconfig"
 
+source "drivers/tdm/Kconfig"
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index cd546eb..362b5ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+= infiniband/
 obj-$(CONFIG_SGI_SN)		+= sn/
 obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
+obj-$(CONFIG_TDM)		+= tdm/
 obj-$(CONFIG_SUPERH)		+= sh/
 obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
 ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig
new file mode 100644
index 0000000..8db2b05
--- /dev/null
+++ b/drivers/tdm/Kconfig
@@ -0,0 +1,25 @@
+#
+# TDM subsystem configuration
+#
+
+menuconfig TDM
+	tristate "TDM support"
+	---help---
+	  More information is contained in the directory <file:Documentation/tdm/>,
+	  especially in the file called "summary" there.
+	  If you want TDM support, you should say Y here and also to the
+	  specific driver for your bus adapter(s) below.
+
+	  This TDM support can also be built as a module.  If so, the module
+	  will be called tdm-core.
+
+if TDM
+
+config TDM_DEBUG_CORE
+	bool "TDM Core debugging messages"
+	help
+	  Say Y here if you want the TDM core to produce a bunch of debug
+	  messages to the system log.  Select this if you are having a
+	  problem with TDM support and want to see more of what is going on.
+
+endif # TDM
diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
new file mode 100644
index 0000000..cdda260
--- /dev/null
+++ b/drivers/tdm/tdm-core.c
@@ -0,0 +1,1146 @@
+/* driver/tdm/tdm-core.c
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
+ *
+ * TDM core is the interface between TDM clients and TDM devices.
+ * It is also intended to serve as an interface for line controld
+ * devices later on.
+ *
+ * Author:Hemant Agrawal <hemant@freescale.com>
+ *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
+ *
+ * Modified by Sandeep Kr Singh <sandeep@freescale.com>
+ *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
+ * 1. Added framework based initialization of device.
+ * 2. All the init/run time configuration is now done by framework.
+ * 3. Added channel level operations.
+ *
+ * This program 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 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.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* if read write debug required */
+#undef TDM_CORE_DEBUG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tdm.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/hardirq.h>
+#include <linux/irqflags.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include "device/tdm_fsl.h"
+
+
+static DEFINE_MUTEX(tdm_core_lock);
+static DEFINE_IDR(tdm_adapter_idr);
+/* List of TDM adapters registered with TDM framework */
+LIST_HEAD(adapter_list);
+
+/* List of TDM clients registered with TDM framework */
+LIST_HEAD(driver_list);
+
+/* In case the previous data is not fetched by the client driver, the
+ * de-interleaving function will  discard the old data and rewrite the
+ * new data */
+static int use_latest_tdm_data = 1;
+
+/* this tasklet is created for each adapter instance */
+static void tdm_data_tasklet_fn(unsigned long);
+
+/* tries to match client driver with the adapter */
+static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
+{
+	/* match on an id table if there is one */
+	if (driver->id_table && driver->id_table->name[0]) {
+		if (!(strcmp(driver->id_table->name, adap->name)))
+			return (int)driver->id_table;
+	}
+	return TDM_E_OK;
+}
+
+static int tdm_attach_driver_adap(struct tdm_driver *driver,
+					struct tdm_adapter *adap)
+{
+	/* if driver is already attached to any other adapter, return*/
+	if (driver->adapter && (driver->adapter != adap))
+		return TDM_E_OK;
+
+	driver->adapter = adap;
+
+	if (driver->attach_adapter) {
+		if (driver->attach_adapter(adap) < 0)
+			/* We ignore the return code; if it fails, too bad */
+			pr_err("attach_adapter failed for driver [%s]\n",
+				driver->name);
+	}
+	adap->drv_count++;
+
+	if (!adap->tasklet_conf) {
+		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
+						(unsigned long)adap);
+		adap->tasklet_conf = 1;
+	}
+
+	return TDM_E_OK;
+}
+
+/* Detach client driver and adapter */
+static int tdm_detach_driver_adap(struct tdm_driver *driver,
+					struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+
+	if (!driver->adapter || (driver->adapter != adap))
+		return TDM_E_OK;
+
+	if (!driver->detach_adapter)
+		return TDM_E_OK;
+
+	adap->drv_count--;
+
+	/* If no more driver is registed with the adapter*/
+	if (!adap->drv_count && adap->tasklet_conf) {
+		tasklet_disable(&adap->tdm_data_tasklet);
+		tasklet_kill(&adap->tdm_data_tasklet);
+		adap->tasklet_conf = 0;
+	}
+
+	if (driver->detach_adapter) {
+		if (driver->detach_adapter(adap))
+			pr_err("detach_adapter failed for driver [%s]\n",
+				driver->name);
+	}
+
+	driver->adapter = NULL;
+	return res;
+}
+
+/* TDM adapter Registration/De-registration with TDM framework */
+
+static int tdm_register_adapter(struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+	struct tdm_driver *driver, *next;
+
+	if (!adap) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_init(&adap->adap_lock);
+	INIT_LIST_HEAD(&adap->myports);
+	spin_lock_init(&adap->portlist_lock);
+
+	adap->drv_count = 0;
+	adap->tasklet_conf = 0;
+
+	list_add_tail(&adap->list, &adapter_list);
+
+	/* initialization of driver by framework in default configuration */
+	init_config_adapter(adap);
+
+	/* Notify drivers */
+	pr_info("adapter [%s] registered\n", adap->name);
+	mutex_lock(&tdm_core_lock);
+	list_for_each_entry_safe(driver, next, &driver_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			res = tdm_attach_driver_adap(driver, adap);
+			pr_info(
+			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
+				driver->id, adap->name, adap->id);
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	return res;
+}
+
+/*
+ * tdm_add_adapter - declare tdm adapter, use dynamic device number
+ * @adapter: the adapter to add
+ * Context: can sleep
+ *
+ * This routine is used to declare a TDM adapter
+ * When this returns zero, a new device number will be allocated and stored
+ * in adap->id, and the specified adapter became available for the clients.
+ * Otherwise, a negative errno value is returned.
+ */
+int tdm_add_adapter(struct tdm_adapter *adapter)
+{
+	int id, res = TDM_E_OK;
+	if (!adapter) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+retry:
+	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
+		return -ENOMEM;
+
+	mutex_lock(&tdm_core_lock);
+	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
+	mutex_unlock(&tdm_core_lock);
+
+	if (res < 0) {
+		if (res == -EAGAIN)
+			goto retry;
+		return res;
+	}
+
+	adapter->id = id;
+	return tdm_register_adapter(adapter);
+}
+EXPORT_SYMBOL(tdm_add_adapter);
+
+
+/**
+ * tdm_del_adapter - unregister TDM adapter
+ * @adap: the adapter being unregistered
+ *
+ * This unregisters an TDM adapter which was previously registered
+ * by @tdm_add_adapter.
+ */
+int tdm_del_adapter(struct tdm_adapter *adap)
+{
+	int res = TDM_E_OK;
+	struct tdm_adapter *found;
+	struct tdm_driver *driver, *next;
+
+	if (!adap) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	/* First make sure that this adapter was ever added */
+	mutex_lock(&tdm_core_lock);
+	found = idr_find(&tdm_adapter_idr, adap->id);
+	mutex_unlock(&tdm_core_lock);
+	if (found != adap) {
+		pr_err("tdm-core: attempting to delete unregistered "
+			 "adapter [%s]\n", adap->name);
+		return -EINVAL;
+	}
+
+	/*disable and kill the data processing tasklet */
+	if (adap->tasklet_conf) {
+		tasklet_disable(&adap->tdm_data_tasklet);
+		tasklet_kill(&adap->tdm_data_tasklet);
+		adap->tasklet_conf = 0;
+	}
+
+	/* Detach any active ports. This can't fail, thus we do not
+	   checking the returned value. */
+	mutex_lock(&tdm_core_lock);
+	list_for_each_entry_safe(driver, next, &driver_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			tdm_detach_driver_adap(driver, adap);
+			pr_info(
+			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
+				 driver->id, adap->name, adap->id);
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	mutex_lock(&tdm_core_lock);
+	idr_remove(&tdm_adapter_idr, adap->id);
+	mutex_unlock(&tdm_core_lock);
+
+	pr_debug("adapter [%s] unregistered\n", adap->name);
+
+	list_del(&adap->list);
+	/* Clear the device structure in case this adapter is ever going to be
+	   added again */
+	adap->parent = NULL;
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_del_adapter);
+
+/* TDM Client Drivers Registration/De-registration Functions */
+int tdm_register_driver(struct tdm_driver *driver)
+{
+	int res = TDM_E_OK;
+	struct tdm_adapter *adap, *next;
+
+	list_add_tail(&driver->list, &driver_list);
+
+	mutex_lock(&tdm_core_lock);
+	/* Walk the adapters that are already present */
+	list_for_each_entry_safe(adap, next, &adapter_list, list) {
+		if (tdm_device_match(driver, adap)) {
+			res = tdm_attach_driver_adap(driver, adap);
+			pr_info("TDM Driver(ID=%d)is attached with Adapter"
+				"%s(ID = %d) drv_count=%d", driver->id,
+				adap->name, adap->id, adap->drv_count);
+		break;
+		}
+	}
+	mutex_unlock(&tdm_core_lock);
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_register_driver);
+
+/*
+ * tdm_unregister_driver - unregister TDM client driver from TDM framework
+ * @driver: the driver being unregistered
+ */
+void tdm_unregister_driver(struct tdm_driver *driver)
+{
+	if (!driver) {
+		pr_err("%s:Invalid handle\n", __func__);
+		return;
+	}
+       /* A driver can register to only one adapter,
+	* so no need to browse the list */
+	mutex_lock(&tdm_core_lock);
+	tdm_detach_driver_adap(driver, driver->adapter);
+	mutex_unlock(&tdm_core_lock);
+
+	list_del(&driver->list);
+
+	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
+}
+EXPORT_SYMBOL(tdm_unregister_driver);
+
+/* TDM Framework init and exit */
+static int __init tdm_init(void)
+{
+	pr_info("%s\n", __func__);
+	return TDM_E_OK;
+}
+
+static void __exit tdm_exit(void)
+{
+	pr_info("%s\n", __func__);
+	return;
+}
+
+/* We must initialize early, because some subsystems register tdm drivers
+ * in subsys_initcall() code, but are linked (and initialized) before tdm.
+ */
+postcore_initcall(tdm_init);
+module_exit(tdm_exit);
+
+
+/* Interface to the tdm device/adapter */
+
+/* tdm_adap_send - issue a TDM write
+ * @adap: Handle to TDM device
+ * @buf: Data that will be written to the TDM device
+ * @count: How many bytes to write
+ *
+ * Returns negative errno, or else the number of bytes written.
+ */
+int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count)
+{
+	int res;
+
+	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	if (adap->algo->tdm_write)
+		res = adap->algo->tdm_write(adap, buf, count);
+	else {
+		pr_err("TDM level write not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* If everything went ok (i.e. frame transmitted), return #bytes
+	   transmitted, else error code. */
+	return (res == 1) ? count : res;
+}
+EXPORT_SYMBOL(tdm_adap_send);
+
+/**
+ * tdm_adap_recv - issue a TDM read
+ * @adap: Handle to TDM device
+ * @buf: Where to store data read from TDM device
+ *
+ * Returns negative errno, or else the number of bytes read.
+ */
+int tdm_adap_recv(struct tdm_adapter *adap, void **buf)
+{
+	int res;
+
+	if (adap->algo->tdm_read)
+		res = adap->algo->tdm_read(adap, (u16 **)buf);
+	else {
+		pr_err("TDM level read not supported\n");
+		return -EOPNOTSUPP;
+	}
+	/* If everything went ok (i.e. frame received), return #bytes
+	   transmitted, else error code. */
+	return res;
+}
+
+/**
+ * tdm_adap_get_write_buf - get next write TDM device buffer
+ * @adap: Handle to TDM device
+ * @buf: pointer to TDM device buffer
+ *
+ * Returns negative errno, or else size of the write buffer.
+ */
+int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf)
+{
+	int res;
+
+	if (adap->algo->tdm_get_write_buf) {
+		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
+	} else {
+		pr_err("TDM level write buf get not supported\n");
+		return -EOPNOTSUPP;
+	}
+	/* If everything went ok (i.e. 1 msg received), return #bytes
+	   transmitted, else error code. */
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_get_write_buf);
+
+int tdm_adap_enable(struct tdm_driver *drv)
+{
+	int res;
+	struct tdm_adapter *adap;
+	if (drv == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	adap = drv->adapter;
+
+	if (adap->algo->tdm_enable) {
+		res = adap->algo->tdm_enable(adap);
+	} else {
+		pr_err("TDM level enable not supported\n");
+		return -EOPNOTSUPP;
+	}
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_enable);
+
+int tdm_adap_disable(struct tdm_driver *drv)
+{
+	int res;
+	struct tdm_adapter *adap;
+	if (drv == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	adap = drv->adapter;
+
+	if (adap->algo->tdm_disable) {
+		res = adap->algo->tdm_disable(adap);
+	} else {
+		pr_err("TDM level enable not supported\n");
+		return -EOPNOTSUPP;
+	}
+	return res;
+}
+EXPORT_SYMBOL(tdm_adap_disable);
+
+struct tdm_adapter *tdm_get_adapter(int id)
+{
+	struct tdm_adapter *adapter;
+
+	mutex_lock(&tdm_core_lock);
+	adapter = idr_find(&tdm_adapter_idr, id);
+	if (adapter && !try_module_get(adapter->owner))
+		adapter = NULL;
+
+	mutex_unlock(&tdm_core_lock);
+
+	return adapter;
+}
+EXPORT_SYMBOL(tdm_get_adapter);
+
+void tdm_put_adapter(struct tdm_adapter *adap)
+{
+	module_put(adap->owner);
+}
+EXPORT_SYMBOL(tdm_put_adapter);
+
+
+/* Port Level APIs of TDM Framework */
+unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)
+{
+	struct tdm_port *port;
+	struct tdm_adapter *adap;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+
+	if (driver == NULL) {
+		pr_err("driver NULL\n");
+		return -ENODEV;
+	}
+	if (driver->adapter == NULL) {
+		pr_err("adapter NULL\n");
+		return -ENODEV;
+	}
+
+	adap = tdm_get_adapter(driver->adapter->id);
+	if (!adap)
+		return -ENODEV;
+
+	/* This creates an anonymous tdm_port, which may later be
+	 * pointed to some slot.
+	 *
+	 */
+	port = kzalloc(sizeof(*port), GFP_KERNEL);
+	if (!port) {
+		res = -ENOMEM;
+		goto out;
+	}
+
+	init_waitqueue_head(&port->ch_wait_queue);
+
+
+	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
+	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
+
+	port->in_use = 1;
+
+	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
+	port->driver = driver;
+	port->adapter = adap;
+
+	spin_lock_irqsave(&adap->portlist_lock, flags);
+	list_add_tail(&port->list, &adap->myports);
+	spin_unlock_irqrestore(&adap->portlist_lock, flags);
+
+	INIT_LIST_HEAD(&port->mychannels);
+
+	*h_port = port;
+
+out:
+	return res;
+}
+EXPORT_SYMBOL(tdm_port_open);
+
+unsigned int tdm_port_close(void *h_port)
+{
+	struct tdm_adapter *adap;
+	struct tdm_driver *driver;
+	struct tdm_port *port;
+	struct tdm_channel *temp, *channel;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+
+	driver =  port->driver;
+
+	if (driver == NULL) {
+		pr_err("driver NULL\n");
+		res = -ENODEV;
+		goto out;
+	}
+	if (driver->adapter == NULL) {
+		pr_err("adapter NULL\n");
+		res = -ENODEV;
+		goto out;
+	}
+
+	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
+	if (channel)
+		if (channel->in_use) {
+			pr_err("%s: Cannot close port. Channel in use\n",
+								__func__);
+			res = -ENXIO;
+			goto out;
+			}
+	}
+	adap = driver->adapter;
+
+	spin_lock_irqsave(&adap->portlist_lock, flags);
+	list_del(&port->list);
+	spin_unlock_irqrestore(&adap->portlist_lock, flags);
+
+	if (port->p_port_data != NULL) {
+		int i;
+		struct tdm_bd *ch_bd;
+
+		/* If the tdm is in channelised mode,
+		de-allocate the channelised buffer */
+		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
+		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
+			ch_bd->flag = 0;
+			ch_bd++;
+		}
+		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
+		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
+			ch_bd->flag = 0;
+			ch_bd++;
+		}
+		kfree(port->p_port_data);
+	}
+	kfree(port);
+	return res;
+out:
+	if (port)
+		kfree(port->p_port_data);
+	kfree(port);
+	return res;
+}
+EXPORT_SYMBOL(tdm_port_close);
+
+unsigned int tdm_channel_read(void *h_port, void *h_channel,
+				void *p_data, u16 *size)
+{
+	struct tdm_port *port;
+	struct tdm_channel *channel;
+	struct tdm_bd *rx_bd;
+	unsigned long flags;
+	int i, res = TDM_E_OK;
+	unsigned short *buf, *buf1;
+	port = (struct tdm_port *)h_port;
+	channel = (struct tdm_channel *)h_channel;
+
+	if ((port && channel) == 0) { /* invalid handle*/
+		pr_err("%s:Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	if (!port->in_use)
+		return -EIO;
+	if (!channel->p_ch_data || !channel->in_use)
+		return -EIO;
+
+	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
+	rx_bd = channel->p_ch_data->rx_out_data;
+
+	if (rx_bd->flag) {
+		*size = rx_bd->length;
+		buf = (u16 *) p_data;
+		buf1 = (u16 *)rx_bd->p_data;
+		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
+			buf[i] = buf1[i];
+		rx_bd->flag = 0;
+		rx_bd->offset = 0;
+		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
+				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
+
+	} else {
+		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
+						flags);
+		pr_info("No Data Available");
+		return -EAGAIN;
+	}
+	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock, flags);
+
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_read);
+
+
+unsigned int tdm_channel_write(void *h_port, void *h_channel,
+				void *p_data, u16 size)
+{
+	struct tdm_port *port;
+	struct tdm_channel *channel;
+	struct tdm_bd *tx_bd;
+	unsigned long flags;
+	int err = TDM_E_OK;
+	port = (struct tdm_port *)h_port;
+	channel = (struct tdm_channel *)h_channel;
+#ifdef TDM_CORE_DEBUG
+	bool data_flag = 0;
+#endif
+
+	if ((port && channel) == 0) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+
+	if (p_data == NULL) { /* invalid data*/
+		pr_err("Invalid Data");
+		return -EFAULT;
+	}
+
+	if (!port->in_use)
+		return -EIO;
+	if (!channel->p_ch_data || !channel->in_use)
+		return -EIO;
+
+	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
+	tx_bd = channel->p_ch_data->tx_in_data;
+
+	if (!tx_bd->flag) {
+		tx_bd->length = size;
+		memcpy(tx_bd->p_data, p_data,
+			size * port->adapter->adapt_cfg.slot_width);
+		tx_bd->flag = 1;
+		tx_bd->offset = 0;
+		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
+				channel->p_ch_data->tx_data_fifo : tx_bd+1;
+		port->port_stat.tx_pkt_count++;
+#ifdef TDM_CORE_DEBUG
+		data_flag = 1;
+#endif
+	} else {
+		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
+						flags);
+		port->port_stat.tx_pkt_drop_count++;
+		pr_err("E_NO_MEMORY -Failed Transmit");
+		return -ENOMEM;
+	}
+	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock, flags);
+
+#ifdef	TDM_CORE_DEBUG
+	if (data_flag) {
+		int k;
+		pr_info("\nTX port:%d - Write - Port TX-%d\n",
+						port->port_id, size);
+		for (k = 0; k < size; k++)
+			pr_info("%x", p_data[k]);
+		pr_info("\n");
+	}
+#endif
+	return err;
+}
+EXPORT_SYMBOL(tdm_channel_write);
+
+wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port)
+{
+	struct tdm_port *port;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return NULL;
+	}
+
+	return &port->ch_wait_queue;
+
+}
+EXPORT_SYMBOL(tdm_port_get_wait_queue);
+
+/* Driver Function for select and poll. Based on Port no, it sleeps on
+ * waitqueue */
+unsigned int tdm_port_poll(void *h_port, unsigned int wait_time)
+{
+	struct tdm_port *port;
+	unsigned long timeout = msecs_to_jiffies(wait_time);
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+	if (!port->p_port_data || !port->in_use)
+		return -EIO;
+
+	if (port->p_port_data->rx_out_data->flag) {
+		pr_debug("Data Available");
+		return TDM_E_OK;
+	}
+	if (timeout) {
+		wait_event_interruptible_timeout(port->ch_wait_queue,
+					  port->p_port_data->rx_out_data->flag,
+					  timeout);
+
+		if (port->p_port_data->rx_out_data->flag) {
+			pr_debug("Data Available");
+			return TDM_E_OK;
+		}
+	}
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(tdm_port_poll);
+
+unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
+{
+	struct tdm_port *port;
+	int port_num;
+	port = (struct tdm_port *)h_port;
+
+	if (port == NULL || portStat == NULL) { /* invalid handle*/
+		pr_err("Invalid Handle");
+		return -ENXIO;
+	}
+	port_num =  port->port_id;
+
+	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
+
+	pr_info("TDM Port %d Get Stats", port_num);
+
+	return TDM_E_OK;
+}
+EXPORT_SYMBOL(tdm_port_get_stats);
+
+/* Data handling functions */
+
+static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
+{
+	struct tdm_port *port, *next;
+	struct tdm_channel *channel, *temp;
+	struct tdm_bd	*ch_bd;
+
+	int i, buf_size, ch_data_len;
+	u16 *input_tdm_buffer;
+	u16 *pcm_buffer;
+	int slot_width;
+	int frame_ch_data_size;
+	bool ch_data;
+	int bytes_in_fifo_per_frame;
+	int bytes_slot_offset;
+
+	ch_data_len = NUM_SAMPLES_PER_FRAME;
+	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
+	ch_data = 0;
+
+	if (!adap) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	slot_width = adap->adapt_cfg.slot_width;
+	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
+	if (buf_size <= 0 || !input_tdm_buffer)
+		return -EINVAL;
+
+	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
+	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
+
+	/* de-interleaving for all ports*/
+	list_for_each_entry_safe(port, next, &adap->myports, list) {
+
+		/* if the port is not open */
+		if (!port->in_use)
+			continue;
+
+		list_for_each_entry_safe(channel, temp, &port->mychannels,
+							list) {
+		/* if the channel is not open */
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+		ch_bd = channel->p_ch_data->rx_in_data;
+		spin_lock(&channel->p_ch_data->rx_channel_lock);
+			/*if old data is to be discarded */
+		if (use_latest_tdm_data)
+			if (ch_bd->flag) {
+				ch_bd->flag = 0;
+				ch_bd->offset = 0;
+				if (ch_bd == channel->p_ch_data->rx_out_data)
+					channel->p_ch_data->rx_out_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->rx_data_fifo
+						: ch_bd+1;
+					port->port_stat.rx_pkt_drop_count++;
+				}
+			/* if the bd is empty */
+			if (!ch_bd->flag) {
+				if (ch_bd->offset == 0)
+					ch_bd->length = port->rx_max_frames;
+
+				pcm_buffer = ch_bd->p_data + ch_bd->offset;
+				/* De-interleaving the data */
+				for (i = 0; i < ch_data_len; i++) {
+					pcm_buffer[i]
+					= input_tdm_buffer[i*bytes_slot_offset +
+						channel->ch_id];
+				}
+				ch_bd->offset += ch_data_len * slot_width;
+
+				if (ch_bd->offset >=
+					(ch_bd->length - frame_ch_data_size)*
+						(adap->adapt_cfg.slot_width)) {
+					ch_bd->flag = 1;
+					ch_bd->offset = 0;
+					channel->p_ch_data->rx_in_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->rx_data_fifo
+						: ch_bd+1;
+					ch_data = 1;
+				}
+			} else {
+				port->port_stat.rx_pkt_drop_count++;
+			}
+		spin_unlock(&channel->p_ch_data->rx_channel_lock);
+		}
+
+		if (ch_data) {
+			/*	Wake up the Port Data Poll event */
+			wake_up_interruptible(&port->ch_wait_queue);
+#ifdef	TDM_CORE_DEBUG
+			pr_info("Port RX-%d-%d\n", channel->ch_id, ch_data_len);
+			for (i = 0; i < ch_data_len; i++)
+				pr_info("%x", pcm_buffer[i]);
+			pr_info("\n");
+#endif
+			port->port_stat.rx_pkt_count++;
+			ch_data = 0;
+		}
+	}
+	return TDM_E_OK;
+}
+
+static int tdm_data_tx_interleave(struct tdm_adapter *adap)
+{
+	struct tdm_port *port, *next;
+	struct tdm_channel *channel, *temp;
+	struct tdm_bd	*ch_bd;
+	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
+	bool last_data = 0;
+	u16 *output_tdm_buffer;
+	u16 *pcm_buffer;
+	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
+	int bytes_in_fifo_per_frame;
+	int bytes_slot_offset;
+
+#ifdef TDM_CORE_DEBUG
+	u8	data_flag = 0;
+#endif
+
+	if (adap == NULL) { /* invalid handle*/
+		pr_err("%s: Invalid Handle\n", __func__);
+		return -ENXIO;
+	}
+
+	buf_size = tdm_adap_get_write_buf(adap, (void **)&output_tdm_buffer);
+	if (buf_size <= 0 || !output_tdm_buffer)
+		return -EINVAL;
+
+	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
+	bytes_slot_offset = bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
+
+
+	memset(output_tdm_buffer, 0, sizeof(buf_size));
+
+	list_for_each_entry_safe(port, next, &adap->myports, list) {
+
+		/* check if the port is open */
+		if (!port->in_use)
+			continue;
+
+		list_for_each_entry_safe(channel, temp, &port->mychannels,
+								list) {
+		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
+
+
+		/* if the channel is open */
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+
+		spin_lock(&channel->p_ch_data->tx_channel_lock);
+		if (!channel->in_use || !channel->p_ch_data)
+			continue;
+			ch_bd = channel->p_ch_data->tx_out_data;
+			if (ch_bd->flag) {
+				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
+						ch_bd->offset);
+				/*if the buffer has less frames than required */
+				if (frame_ch_data_size >=
+					((ch_bd->length) - (ch_bd->offset/
+						adap->adapt_cfg.slot_width))) {
+					ch_data_len =
+					(ch_bd->length) - (ch_bd->offset/
+						adap->adapt_cfg.slot_width);
+					last_data = 1;
+				} else {
+					ch_data_len = frame_ch_data_size;
+				}
+				/* Interleaving the data */
+				for (i = 0; i < ch_data_len; i++) {
+					/* TODO- need to be generic for any size
+					   assignment*/
+					output_tdm_buffer[channel->ch_id +
+						bytes_slot_offset * i] =
+								pcm_buffer[i];
+				}
+				/* If all the data of this buffer is
+							transmitted */
+				if (last_data) {
+					ch_bd->flag = 0;
+					ch_bd->offset = 0;
+					channel->p_ch_data->tx_out_data =
+						ch_bd->wrap ?
+						channel->p_ch_data->tx_data_fifo
+						: ch_bd+1;
+					port->port_stat.tx_pkt_conf_count++;
+				} else {
+					ch_bd->offset += ch_data_len *
+						(adap->adapt_cfg.slot_width);
+				}
+#ifdef	TDM_CORE_DEBUG
+				data_flag = 1;
+#endif
+			}
+		spin_unlock(&channel->p_ch_data->tx_channel_lock);
+		}
+	}
+
+#ifdef	TDM_CORE_DEBUG
+	if (data_flag) {
+		pr_info("TX-TDM Interleaved Data-\n");
+		for (i = 0; i < 64; i++)
+			pr_info("%x", output_tdm_buffer[i]);
+		pr_info("\n");
+	  }
+#endif
+	return TDM_E_OK;
+}
+
+/* Channel Level APIs of TDM Framework */
+int tdm_channel_open(u16 chanid, u16 ch_width, struct tdm_port *port,
+				void **h_channel)
+{
+	struct tdm_channel *channel, *temp;
+	unsigned long		flags;
+	struct tdm_ch_data	*p_ch_data;
+	int res = TDM_E_OK;
+
+	if (!(port && h_channel)) {
+		pr_err("%s: Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ch_width != 1) {
+		pr_err("%s: Mode not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
+		if (channel->ch_id == chanid) {
+			pr_err("%s: Channel %d already open\n",
+						__func__, chanid);
+			return -EINVAL;
+		}
+	}
+
+	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+	if (!channel) {
+		res = -ENOMEM;
+		goto out;
+	}
+
+	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
+	if (!p_ch_data) {
+		res = -ENOMEM;
+		goto outdata;
+	}
+
+	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
+	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
+
+	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
+	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
+	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
+	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
+	spin_lock_init(&p_ch_data->rx_channel_lock);
+	spin_lock_init(&p_ch_data->tx_channel_lock);
+
+	channel->p_ch_data = p_ch_data;
+
+	channel->ch_id = chanid;
+	channel->ch_cfg.first_slot = chanid;
+	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized mode and
+						configurable for other modes */
+	channel->port = port;
+	channel->in_use = 1;
+
+	spin_lock_irqsave(&port->ch_list_lock, flags);
+	list_add_tail(&channel->list, &port->mychannels);
+	spin_unlock_irqrestore(&port->ch_list_lock, flags);
+
+	*h_channel = channel;
+
+	return res;
+
+outdata:
+	kfree(channel);
+out:
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_open);
+
+int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
+				struct tdm_channel *h_channel)
+{
+	struct tdm_channel *channel;
+	unsigned long		flags;
+	int res = TDM_E_OK;
+	channel = h_channel;
+
+	if (!(port && channel)) {
+		pr_err("%s: Invalid handle\n", __func__);
+		res = -EINVAL;
+		goto out;
+	}
+
+	if (ch_width != 1) {
+		pr_err("%s: Mode not supported\n", __func__);
+		res = -EINVAL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&port->ch_list_lock, flags);
+	list_del(&channel->list);
+	spin_unlock_irqrestore(&port->ch_list_lock, flags);
+
+out:
+	if (channel)
+		kfree(channel->p_ch_data);
+	kfree(channel);
+	return res;
+}
+EXPORT_SYMBOL(tdm_channel_close);
+
+void init_config_adapter(struct tdm_adapter *adap)
+{
+	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
+		.loopback = e_TDM_PROCESS_NORMAL,
+		.num_ch = NUM_CHANNELS,
+		.ch_size_type = CHANNEL_16BIT_LIN,
+		.frame_len = NUM_SAMPLES_PER_FRAME,
+		.num_frames = NUM_SAMPLES_PER_FRAME,
+		.adap_mode = e_TDM_ADAPTER_MODE_NONE
+			 };
+
+	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 + 1;
+
+	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
+		sizeof(struct fsl_tdm_adapt_cfg));
+
+	return;
+}
+EXPORT_SYMBOL(init_config_adapter);
+
+static void tdm_data_tasklet_fn(unsigned long data)
+{
+	struct tdm_adapter *adapter;
+	adapter = (struct tdm_adapter *)data;
+	if (adapter != NULL) {
+		tdm_data_tx_interleave(adapter);
+		tdm_data_rx_deinterleave(adapter);
+	}
+}
+
+
+MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
+	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
+MODULE_DESCRIPTION("TDM Driver Framework Core");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index ae28e93..dc1a655 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -416,6 +416,17 @@ struct i2c_device_id {
 			__attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* tdm */
+
+#define TDM_NAME_SIZE   20
+#define TDM_MODULE_PREFIX "tdm:"
+
+struct tdm_device_id {
+	char name[TDM_NAME_SIZE];
+	kernel_ulong_t driver_data      /* Data private to the driver */
+			__attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+
 /* spi */
 
 #define SPI_NAME_SIZE	32
diff --git a/include/linux/tdm.h b/include/linux/tdm.h
new file mode 100644
index 0000000..8cf4ef5
--- /dev/null
+++ b/include/linux/tdm.h
@@ -0,0 +1,347 @@
+/* include/linux/tdm.h
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
+ *
+ * tdm.h - definitions for the tdm-device framework interface
+ *
+ * Author:Hemant Agrawal <hemant@freescale.com>
+ *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
+ *
+ * This program 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 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.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef _LINUX_TDM_H
+#define _LINUX_TDM_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>	/* for struct device */
+#include <linux/sched.h>	/* for completion */
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+
+#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
+#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
+#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
+#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
+
+#define NUM_CHANNELS		16
+#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
+						 channel. Req for voice data */
+#define NUM_MS			10
+#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
+						samples for 1 client buffer */
+#define NUM_OF_TDM_BUF		3
+
+/* General options */
+
+struct tdm_adapt_algorithm;
+struct tdm_adapter;
+struct tdm_port;
+struct tdm_driver;
+
+/* Align addr on a size boundary - adjust address up if needed */
+/* returns min value greater than size which is multiple of alignment */
+static inline int ALIGN_SIZE(u64 size, u32 alignment)
+{
+	return (size + alignment - 1) & (~(alignment - 1));
+}
+
+/**
+ * struct tdm_driver - represent an TDM device driver
+ * @class: What kind of tdm device we instantiate (for detect)
+ * @id:Driver id
+ * @name: Name of the driver
+ * @attach_adapter: Callback for device addition (for legacy drivers)
+ * @detach_adapter: Callback for device removal (for legacy drivers)
+ * @probe: Callback for device binding
+ * @remove: Callback for device unbinding
+ * @shutdown: Callback for device shutdown
+ * @suspend: Callback for device suspend
+ * @resume: Callback for device resume
+ * @command: Callback for sending commands to device
+ * @id_table: List of TDM devices supported by this driver
+ * @list: List of drivers created (for tdm-core use only)
+ */
+struct tdm_driver {
+	unsigned int class;
+	unsigned int id;
+	char name[TDM_NAME_SIZE];
+
+	int (*attach_adapter)(struct tdm_adapter *);
+	int (*detach_adapter)(struct tdm_adapter *);
+
+	/* Standard driver model interfaces */
+	int (*probe)(const struct tdm_device_id *);
+	int (*remove)(void);
+
+	/* driver model interfaces that don't relate to enumeration */
+	void (*shutdown)(void);
+	int (*suspend)(pm_message_t mesg);
+	int (*resume)(void);
+
+	/* a ioctl like command that can be used to perform specific functions
+	 * with the device.
+	 */
+	int (*command)(unsigned int cmd, void *arg);
+
+	const struct tdm_device_id *id_table;
+
+	/* The associated adapter for this driver */
+	struct tdm_adapter *adapter;
+	struct list_head list;
+};
+
+/* tdm per port statistics structure, used for providing and storing tdm port
+ * statistics.
+ */
+struct tdm_port_stats {
+	unsigned int rx_pkt_count;	/* Rx frame count per channel */
+	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
+					 clean space for new buffer */
+	unsigned int tx_pkt_count;	/* Tx frame count per channel */
+	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count per
+					 channel */
+	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel due to
+					 queue full */
+};
+
+
+/* tdm Buffer Descriptor, used for Creating Interleaved and De-interleaved
+ * FIFOs
+ */
+struct tdm_bd {
+	unsigned char flag;		/* BD is full or empty */
+	unsigned char wrap;		/* BD is last in the queue */
+	unsigned short length;	/* Length of Data in BD */
+	/*TODO: use dyanmic memory */
+	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
+	unsigned long offset;	/* Offset of the Data Pointer to be used */
+};
+
+#define TDM_CH_RX_BD_RING_SIZE	3
+#define TDM_CH_TX_BD_RING_SIZE	3
+
+/* tdm RX-TX Channelised Data */
+struct tdm_port_data {
+	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel Data
+								BD Ring */
+	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled by
+						de-interleave function */
+	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
+							read by App */
+	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel Data
+								BD Ring */
+	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
+						 filled by App */
+	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read by
+						interleave function */
+	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
+	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
+};
+
+/* structure tdm_port_cfg - contains configuration params for a port */
+struct tdm_port_cfg {
+	unsigned short port_mode;
+};
+
+/* struct tdm_port - represent an TDM ports for a device */
+struct tdm_port {
+	unsigned short port_id;
+	unsigned short in_use;		/* Port is enabled? */
+	uint16_t rx_max_frames;		/* Received Port frames
+					 before allowing Read Operation in
+					 Port Mode */
+
+	struct tdm_port_stats port_stat;/* A structure parameters defining
+					 TDM port statistics. */
+	struct tdm_port_data *p_port_data;	/* a structure parameters
+						defining tdm channelised data */
+	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data */
+
+	struct tdm_driver *driver;	/* driver for this port */
+	struct tdm_adapter *adapter;	/* adapter for this port */
+	struct list_head list;		/* list of ports */
+	struct list_head mychannels;	/* list of channels, created on this
+					 port*/
+	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
+	struct tdm_port_cfg port_cfg;/* A structure parameters defining
+					 TDM port configuration. */
+};
+
+/* tdm RX-TX Channelised Data */
+struct tdm_ch_data {
+	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data BD
+								Ring */
+	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
+						de-interleave function */
+	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App */
+	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data BD
+								Ring */
+	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
+						App */
+	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
+						interleave function */
+	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
+	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
+};
+
+/* Channel config params */
+struct tdm_ch_cfg {
+	unsigned short num_slots;
+	unsigned short first_slot;
+};
+
+/* struct tdm_channel- represent a TDM channel for a port */
+struct tdm_channel {
+	u16 ch_id;			/* logical channel number */
+	struct list_head list;		/* list of channels in a port*/
+	struct tdm_port *port;		/* port for this channel */
+	u16 in_use;			/* channel is enabled? */
+	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
+	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
+};
+
+/* tdm_adapt_algorithm is for accessing the routines of device */
+struct tdm_adapt_algorithm {
+	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
+	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
+	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
+	int (*tdm_enable)(struct tdm_adapter *);
+	int (*tdm_disable)(struct tdm_adapter *);
+};
+
+/* tdm_adapter_mode is to define in mode of the device */
+enum tdm_adapter_mode {
+	e_TDM_ADAPTER_MODE_NONE = 0x00,
+	e_TDM_ADAPTER_MODE_T1 = 0x01,
+	e_TDM_ADAPTER_MODE_E1 = 0x02,
+	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
+	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
+};
+
+/* tdm_port_mode defines the mode in which the port is configured to operate
+ * It can be channelized/full/fractional.
+ */
+enum tdm_port_mode {
+	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
+	, e_TDM_PORT_FULL = 1		/* Full mode */
+	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
+};
+
+/* tdm_process_mode used for testing the tdm device in normal mode or internal
+ * loopback or external loopback
+ */
+enum tdm_process_mode {
+	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
+	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
+	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
+};
+
+
+/* TDM configuration parameters */
+struct fsl_tdm_adapt_cfg {
+	u8 num_ch;		/* Number of channels in this adpater */
+	u8 ch_size_type;		/* reciever/transmit channel
+						size for all channels */
+	u8 slot_width;		/* 1 or 2 Is defined by channel type */
+	u8 frame_len;		/* Length of frame in samples */
+	u32 num_frames;
+	u8 loopback;			/* loopback or normal */
+	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
+						4 = E1-FULL */
+	int max_num_ports;		/* Not Used: Max Number of ports that
+					can be created on this adapter */
+	int max_timeslots;		/* Max Number of timeslots that are
+					supported on this adapter */
+};
+
+/*
+ * tdm_adapter is the structure used to identify a physical tdm device along
+ * with the access algorithms necessary to access it.
+ */
+struct tdm_adapter {
+	struct module *owner;	/* owner of the adapter module */
+	unsigned int id;	/* Adapter Id */
+	unsigned int class;	/* classes to allow probing for */
+	unsigned int drv_count;	/* Number of drivers associated with the
+				 adapter */
+
+	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
+						 adapter*/
+
+	char name[TDM_NAME_SIZE];	/* Name of Adapter */
+	struct mutex adap_lock;
+	struct device *parent;		/*Not Used*/
+
+	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
+						 data processing*/
+	int tasklet_conf;	/* flag for tasklet configuration */
+	int tdm_rx_flag;
+
+	struct list_head myports;	/* list of ports, created on this
+					 adapter */
+	struct list_head list;
+	spinlock_t portlist_lock;	/* Spin Lock for port_list */
+	void *data;
+	struct fsl_tdm_adapt_cfg adapt_cfg;
+};
+
+static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
+{
+	return dev->data;
+}
+
+static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
+{
+	dev->data = data;
+}
+
+/* functions exported by tdm.o */
+
+extern int tdm_add_adapter(struct tdm_adapter *);
+extern int tdm_del_adapter(struct tdm_adapter *);
+extern int tdm_register_driver(struct tdm_driver *);
+extern void tdm_del_driver(struct tdm_driver *);
+extern void tdm_unregister_driver(struct tdm_driver *);
+extern void init_config_adapter(struct tdm_adapter *);
+
+extern unsigned int tdm_port_open(struct tdm_driver *, void **);
+extern unsigned int tdm_port_close(void *);
+extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
+extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
+extern unsigned int tdm_channel_write(void *, void * , void *, u16);
+extern unsigned int tdm_port_poll(void *, unsigned int);
+
+extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
+extern int tdm_channel_close(u16, u16, struct tdm_port *,
+						struct tdm_channel *);
+
+static inline int tdm_add_driver(struct tdm_driver *driver)
+{
+	return tdm_register_driver(driver);
+}
+
+extern struct tdm_adapter *tdm_get_adapter(int id);
+extern void tdm_put_adapter(struct tdm_adapter *adap);
+
+#endif /* __KERNEL__ */
+
+#define TDM_E_OK 0
+
+#endif /* _LINUX_TDM_H */
-- 
1.6.5.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: [PATCH][2/3][RFC] TDM Framework
  2012-03-10 12:57 [PATCH][2/3][RFC] TDM Framework Poonam Aggrwal
@ 2012-04-23  8:00 ` Aggrwal Poonam-B10812
  2012-04-24  0:34 ` Scott Wood
  2012-04-25  5:49 ` Benjamin Herrenschmidt
  2 siblings, 0 replies; 6+ messages in thread
From: Aggrwal Poonam-B10812 @ 2012-04-23  8:00 UTC (permalink / raw)
  To: Aggrwal Poonam-B10812, linuxppc-dev; +Cc: Singh Sandeep-B37400

> -----Original Message-----
> From: Aggrwal Poonam-B10812
> Sent: Saturday, March 10, 2012 6:27 PM
> To: linuxppc-dev@lists.ozlabs.org
> Cc: Singh Sandeep-B37400; Aggrwal Poonam-B10812
> Subject: [PATCH][2/3][RFC] TDM Framework
>=20

Any feedback on this patchset?

> From: Sandeep Singh <Sandeep@freescale.com>
>=20
> TDM Framework is an attempt to provide a platform independent layer which
> can offer a standard interface  for TDM access to different client
> modules.
> Beneath, the framework layer can house different types of TDM drivers to
> handle various TDM devices, the hardware intricacies of the devices being
> completely taken care by TDM drivers.
>=20
> This framework layer will allow any type of TDM device to hook with it.
> For example Freescale controller as on MPC8315, UCC based TDM controller,
> or any other controller.
>=20
> The main functions of this Framework are:
> - provides interface to TDM clients to access TDM functionalities.
> - provides standard interface for TDM drivers to hook with the framework.
> - handles various data handling stuff and buffer management.
>=20
> In future this Framework will be extended to provide Interface for Line
> control devices also. For example SLIC, E1/T1 Framers etc.
>=20
> Limitations/Future Work
> ---------------------------
> 1. Presently the framework supports only Single Port channelised mode.
> 2. Also the configurability options are limited which will be extended
> later on.
> 3. Only kernel mode TDM clients are supported currently. Support for User
> mode clients will be added later.
>=20
> Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
> Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
> ---
>  A couple of todos' are left in the patch, we are working on it and will
> be addressed in the updated patch set.
>  drivers/Kconfig                 |    1 +
>  drivers/Makefile                |    1 +
>  drivers/tdm/Kconfig             |   25 +
>  drivers/tdm/tdm-core.c          | 1146
> +++++++++++++++++++++++++++++++++++++++
>  include/linux/mod_devicetable.h |   11 +
>  include/linux/tdm.h             |  347 ++++++++++++
>  6 files changed, 1531 insertions(+), 0 deletions(-)  create mode 100644
> drivers/tdm/Kconfig  create mode 100644 drivers/tdm/tdm-core.c  create
> mode 100644 include/linux/tdm.h
>=20
> diff --git a/drivers/Kconfig b/drivers/Kconfig index ad6c1eb..25f7f5b
> 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>=20
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>=20
> +source "drivers/tdm/Kconfig"
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile index cd546eb..362b5ed
> 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+=3D infiniband/
>  obj-$(CONFIG_SGI_SN)		+=3D sn/
>  obj-y				+=3D firmware/
>  obj-$(CONFIG_CRYPTO)		+=3D crypto/
> +obj-$(CONFIG_TDM)		+=3D tdm/
>  obj-$(CONFIG_SUPERH)		+=3D sh/
>  obj-$(CONFIG_ARCH_SHMOBILE)	+=3D sh/
>  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
> diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig new file mode
> 100644 index 0000000..8db2b05
> --- /dev/null
> +++ b/drivers/tdm/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# TDM subsystem configuration
> +#
> +
> +menuconfig TDM
> +	tristate "TDM support"
> +	---help---
> +	  More information is contained in the directory
> <file:Documentation/tdm/>,
> +	  especially in the file called "summary" there.
> +	  If you want TDM support, you should say Y here and also to the
> +	  specific driver for your bus adapter(s) below.
> +
> +	  This TDM support can also be built as a module.  If so, the
> module
> +	  will be called tdm-core.
> +
> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going
> on.
> +
> +endif # TDM
> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c new file
> mode 100644 index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program 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 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.
> + *
> + * 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.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"
> +
> +
> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data =3D 1;
> +
> +/* this tasklet is created for each adapter instance */ static void
> +tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */ static int
> +tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap) {
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter !=3D adap))
> +		return TDM_E_OK;
> +
> +	driver->adapter =3D adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +	adap->drv_count++;
> +
> +	if (!adap->tasklet_conf) {
> +		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
> +						(unsigned long)adap);
> +		adap->tasklet_conf =3D 1;
> +	}
> +
> +	return TDM_E_OK;
> +}
> +
> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res =3D TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter !=3D adap))
> +		return TDM_E_OK;
> +
> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;
> +
> +	/* If no more driver is registed with the adapter*/
> +	if (!adap->drv_count && adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf =3D 0;
> +	}
> +
> +	if (driver->detach_adapter) {
> +		if (driver->detach_adapter(adap))
> +			pr_err("detach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +
> +	driver->adapter =3D NULL;
> +	return res;
> +}
> +
> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap) {
> +	int res =3D TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count =3D 0;
> +	adap->tasklet_conf =3D 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration
> */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res =3D tdm_attach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=3D%d) is attached with Adapter %s(ID =3D %d)\n",
> +				driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +
> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> + * @adapter: the adapter to add
> + * Context: can sleep
> + *
> + * This routine is used to declare a TDM adapter
> + * When this returns zero, a new device number will be allocated and
> +stored
> + * in adap->id, and the specified adapter became available for the
> clients.
> + * Otherwise, a negative errno value is returned.
> + */
> +int tdm_add_adapter(struct tdm_adapter *adapter) {
> +	int id, res =3D TDM_E_OK;
> +	if (!adapter) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +retry:
> +	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) =3D=3D 0)
> +		return -ENOMEM;
> +
> +	mutex_lock(&tdm_core_lock);
> +	res =3D idr_get_new(&tdm_adapter_idr, adapter, &id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	if (res < 0) {
> +		if (res =3D=3D -EAGAIN)
> +			goto retry;
> +		return res;
> +	}
> +
> +	adapter->id =3D id;
> +	return tdm_register_adapter(adapter);
> +}
> +EXPORT_SYMBOL(tdm_add_adapter);
> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap) {
> +	int res =3D TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found =3D idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found !=3D adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf =3D 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=3D%d) is detached from Adapter %s(ID =3D %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	pr_debug("adapter [%s] unregistered\n", adap->name);
> +
> +	list_del(&adap->list);
> +	/* Clear the device structure in case this adapter is ever going to
> be
> +	   added again */
> +	adap->parent =3D NULL;
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_del_adapter);
> +
> +/* TDM Client Drivers Registration/De-registration Functions */ int
> +tdm_register_driver(struct tdm_driver *driver) {
> +	int res =3D TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res =3D tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=3D%d)is attached with Adapter"
> +				"%s(ID =3D %d) drv_count=3D%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM
> +framework
> + * @driver: the driver being unregistered  */ void
> +tdm_unregister_driver(struct tdm_driver *driver) {
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */
> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name); }
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}
> +
> +/* We must initialize early, because some subsystems register tdm
> +drivers
> + * in subsys_initcall() code, but are linked (and initialized) before
> tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);
> +
> +
> +/* Interface to the tdm device/adapter */
> +
> +/* tdm_adap_send - issue a TDM write
> + * @adap: Handle to TDM device
> + * @buf: Data that will be written to the TDM device
> + * @count: How many bytes to write
> + *
> + * Returns negative errno, or else the number of bytes written.
> + */
> +int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count) {
> +	int res;
> +
> +	if ((adap =3D=3D NULL) || (buf =3D=3D NULL)) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (adap->algo->tdm_write)
> +		res =3D adap->algo->tdm_write(adap, buf, count);
> +	else {
> +		pr_err("TDM level write not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* If everything went ok (i.e. frame transmitted), return #bytes
> +	   transmitted, else error code. */
> +	return (res =3D=3D 1) ? count : res;
> +}
> +EXPORT_SYMBOL(tdm_adap_send);
> +
> +/**
> + * tdm_adap_recv - issue a TDM read
> + * @adap: Handle to TDM device
> + * @buf: Where to store data read from TDM device
> + *
> + * Returns negative errno, or else the number of bytes read.
> + */
> +int tdm_adap_recv(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_read)
> +		res =3D adap->algo->tdm_read(adap, (u16 **)buf);
> +	else {
> +		pr_err("TDM level read not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. frame received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +
> +/**
> + * tdm_adap_get_write_buf - get next write TDM device buffer
> + * @adap: Handle to TDM device
> + * @buf: pointer to TDM device buffer
> + *
> + * Returns negative errno, or else size of the write buffer.
> + */
> +int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf) {
> +	int res;
> +
> +	if (adap->algo->tdm_get_write_buf) {
> +		res =3D adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
> +	} else {
> +		pr_err("TDM level write buf get not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. 1 msg received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_get_write_buf);
> +
> +int tdm_adap_enable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv =3D=3D NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap =3D drv->adapter;
> +
> +	if (adap->algo->tdm_enable) {
> +		res =3D adap->algo->tdm_enable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_enable);
> +
> +int tdm_adap_disable(struct tdm_driver *drv) {
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv =3D=3D NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap =3D drv->adapter;
> +
> +	if (adap->algo->tdm_disable) {
> +		res =3D adap->algo->tdm_disable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_disable);
> +
> +struct tdm_adapter *tdm_get_adapter(int id) {
> +	struct tdm_adapter *adapter;
> +
> +	mutex_lock(&tdm_core_lock);
> +	adapter =3D idr_find(&tdm_adapter_idr, id);
> +	if (adapter && !try_module_get(adapter->owner))
> +		adapter =3D NULL;
> +
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return adapter;
> +}
> +EXPORT_SYMBOL(tdm_get_adapter);
> +
> +void tdm_put_adapter(struct tdm_adapter *adap) {
> +	module_put(adap->owner);
> +}
> +EXPORT_SYMBOL(tdm_put_adapter);
> +
> +
> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port) {
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res =3D TDM_E_OK;
> +
> +	if (driver =3D=3D NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter =3D=3D NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	adap =3D tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port =3D kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res =3D -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames =3D NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode =3D e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use =3D 1;
> +
> +	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
> +	port->driver =3D driver;
> +	port->adapter =3D adap;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_add_tail(&port->list, &adap->myports);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	INIT_LIST_HEAD(&port->mychannels);
> +
> +	*h_port =3D port;
> +
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_open);
> +
> +unsigned int tdm_port_close(void *h_port) {
> +	struct tdm_adapter *adap;
> +	struct tdm_driver *driver;
> +	struct tdm_port *port;
> +	struct tdm_channel *temp, *channel;
> +	unsigned long		flags;
> +	int res =3D TDM_E_OK;
> +	port =3D (struct tdm_port *)h_port;
> +
> +	if (port =3D=3D NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	driver =3D  port->driver;
> +
> +	if (driver =3D=3D NULL) {
> +		pr_err("driver NULL\n");
> +		res =3D -ENODEV;
> +		goto out;
> +	}
> +	if (driver->adapter =3D=3D NULL) {
> +		pr_err("adapter NULL\n");
> +		res =3D -ENODEV;
> +		goto out;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res =3D -ENXIO;
> +			goto out;
> +			}
> +	}
> +	adap =3D driver->adapter;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_del(&port->list);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	if (port->p_port_data !=3D NULL) {
> +		int i;
> +		struct tdm_bd *ch_bd;
> +
> +		/* If the tdm is in channelised mode,
> +		de-allocate the channelised buffer */
> +		ch_bd =3D &(port->p_port_data->rx_data_fifo[0]);
> +		for (i =3D 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
> +			ch_bd->flag =3D 0;
> +			ch_bd++;
> +		}
> +		ch_bd =3D &(port->p_port_data->tx_data_fifo[0]);
> +		for (i =3D 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
> +			ch_bd->flag =3D 0;
> +			ch_bd++;
> +		}
> +		kfree(port->p_port_data);
> +	}
> +	kfree(port);
> +	return res;
> +out:
> +	if (port)
> +		kfree(port->p_port_data);
> +	kfree(port);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_close);
> +
> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res =3D TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port =3D (struct tdm_port *)h_port;
> +	channel =3D (struct tdm_channel *)h_channel;
> +
> +	if ((port && channel) =3D=3D 0) { /* invalid handle*/
> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> +	rx_bd =3D channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size =3D rx_bd->length;
> +		buf =3D (u16 *) p_data;
> +		buf1 =3D (u16 *)rx_bd->p_data;
> +		for (i =3D 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] =3D buf1[i];
> +		rx_bd->flag =3D 0;
> +		rx_bd->offset =3D 0;
> +		channel->p_ch_data->rx_out_data =3D (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> flags);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_read);
> +
> +
> +unsigned int tdm_channel_write(void *h_port, void *h_channel,
> +				void *p_data, u16 size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *tx_bd;
> +	unsigned long flags;
> +	int err =3D TDM_E_OK;
> +	port =3D (struct tdm_port *)h_port;
> +	channel =3D (struct tdm_channel *)h_channel; #ifdef TDM_CORE_DEBUG
> +	bool data_flag =3D 0;
> +#endif
> +
> +	if ((port && channel) =3D=3D 0) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	if (p_data =3D=3D NULL) { /* invalid data*/
> +		pr_err("Invalid Data");
> +		return -EFAULT;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
> +	tx_bd =3D channel->p_ch_data->tx_in_data;
> +
> +	if (!tx_bd->flag) {
> +		tx_bd->length =3D size;
> +		memcpy(tx_bd->p_data, p_data,
> +			size * port->adapter->adapt_cfg.slot_width);
> +		tx_bd->flag =3D 1;
> +		tx_bd->offset =3D 0;
> +		channel->p_ch_data->tx_in_data =3D (tx_bd->wrap) ?
> +				channel->p_ch_data->tx_data_fifo : tx_bd+1;
> +		port->port_stat.tx_pkt_count++;
> +#ifdef TDM_CORE_DEBUG
> +		data_flag =3D 1;
> +#endif
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> +						flags);
> +		port->port_stat.tx_pkt_drop_count++;
> +		pr_err("E_NO_MEMORY -Failed Transmit");
> +		return -ENOMEM;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> flags);
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		int k;
> +		pr_info("\nTX port:%d - Write - Port TX-%d\n",
> +						port->port_id, size);
> +		for (k =3D 0; k < size; k++)
> +			pr_info("%x", p_data[k]);
> +		pr_info("\n");
> +	}
> +#endif
> +	return err;
> +}
> +EXPORT_SYMBOL(tdm_channel_write);
> +
> +wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port) {
> +	struct tdm_port *port;
> +	port =3D (struct tdm_port *)h_port;
> +
> +	if (port =3D=3D NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return NULL;
> +	}
> +
> +	return &port->ch_wait_queue;
> +
> +}
> +EXPORT_SYMBOL(tdm_port_get_wait_queue);
> +
> +/* Driver Function for select and poll. Based on Port no, it sleeps on
> + * waitqueue */
> +unsigned int tdm_port_poll(void *h_port, unsigned int wait_time) {
> +	struct tdm_port *port;
> +	unsigned long timeout =3D msecs_to_jiffies(wait_time);
> +	port =3D (struct tdm_port *)h_port;
> +
> +	if (port =3D=3D NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	if (!port->p_port_data || !port->in_use)
> +		return -EIO;
> +
> +	if (port->p_port_data->rx_out_data->flag) {
> +		pr_debug("Data Available");
> +		return TDM_E_OK;
> +	}
> +	if (timeout) {
> +		wait_event_interruptible_timeout(port->ch_wait_queue,
> +					  port->p_port_data->rx_out_data->flag,
> +					  timeout);
> +
> +		if (port->p_port_data->rx_out_data->flag) {
> +			pr_debug("Data Available");
> +			return TDM_E_OK;
> +		}
> +	}
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(tdm_port_poll);
> +
> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats
> +*portStat) {
> +	struct tdm_port *port;
> +	int port_num;
> +	port =3D (struct tdm_port *)h_port;
> +
> +	if (port =3D=3D NULL || portStat =3D=3D NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =3D  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len =3D NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size =3D NUM_SAMPLES_PER_FRAME;
> +	ch_data =3D 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width =3D adap->adapt_cfg.slot_width;
> +	buf_size =3D tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <=3D 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame =3D buf_size/frame_ch_data_size;
> +	bytes_slot_offset =3D bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd =3D channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag =3D 0;
> +				ch_bd->offset =3D 0;
> +				if (ch_bd =3D=3D channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =3D
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset =3D=3D 0)
> +					ch_bd->length =3D port->rx_max_frames;
> +
> +				pcm_buffer =3D ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i =3D 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					=3D input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset +=3D ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=3D
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag =3D 1;
> +					ch_bd->offset =3D 0;
> +					channel->p_ch_data->rx_in_data =3D
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data =3D 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}
> +
> +		if (ch_data) {
> +			/*	Wake up the Port Data Poll event */
> +			wake_up_interruptible(&port->ch_wait_queue);
> +#ifdef	TDM_CORE_DEBUG
> +			pr_info("Port RX-%d-%d\n", channel->ch_id,
> ch_data_len);
> +			for (i =3D 0; i < ch_data_len; i++)
> +				pr_info("%x", pcm_buffer[i]);
> +			pr_info("\n");
> +#endif
> +			port->port_stat.rx_pkt_count++;
> +			ch_data =3D 0;
> +		}
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_data_tx_interleave(struct tdm_adapter *adap) {
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +	int i, buf_size, ch_data_len =3D NUM_SAMPLES_PER_FRAME;
> +	bool last_data =3D 0;
> +	u16 *output_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int frame_ch_data_size =3D NUM_SAMPLES_PER_FRAME;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +#ifdef TDM_CORE_DEBUG
> +	u8	data_flag =3D 0;
> +#endif
> +
> +	if (adap =3D=3D NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	buf_size =3D tdm_adap_get_write_buf(adap, (void
> **)&output_tdm_buffer);
> +	if (buf_size <=3D 0 || !output_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame =3D buf_size/frame_ch_data_size;
> +	bytes_slot_offset =3D
> +bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
> +
> +
> +	memset(output_tdm_buffer, 0, sizeof(buf_size));
> +
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* check if the port is open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +								list) {
> +		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
> +
> +
> +		/* if the channel is open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +
> +		spin_lock(&channel->p_ch_data->tx_channel_lock);
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +			ch_bd =3D channel->p_ch_data->tx_out_data;
> +			if (ch_bd->flag) {
> +				pcm_buffer =3D (u16 *)((uint8_t *)ch_bd->p_data +
> +						ch_bd->offset);
> +				/*if the buffer has less frames than required */
> +				if (frame_ch_data_size >=3D
> +					((ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width))) {
> +					ch_data_len =3D
> +					(ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width);
> +					last_data =3D 1;
> +				} else {
> +					ch_data_len =3D frame_ch_data_size;
> +				}
> +				/* Interleaving the data */
> +				for (i =3D 0; i < ch_data_len; i++) {
> +					/* TODO- need to be generic for any size
> +					   assignment*/
> +					output_tdm_buffer[channel->ch_id +
> +						bytes_slot_offset * i] =3D
> +								pcm_buffer[i];
> +				}
> +				/* If all the data of this buffer is
> +							transmitted */
> +				if (last_data) {
> +					ch_bd->flag =3D 0;
> +					ch_bd->offset =3D 0;
> +					channel->p_ch_data->tx_out_data =3D
> +						ch_bd->wrap ?
> +						channel->p_ch_data->tx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.tx_pkt_conf_count++;
> +				} else {
> +					ch_bd->offset +=3D ch_data_len *
> +						(adap->adapt_cfg.slot_width);
> +				}
> +#ifdef	TDM_CORE_DEBUG
> +				data_flag =3D 1;
> +#endif
> +			}
> +		spin_unlock(&channel->p_ch_data->tx_channel_lock);
> +		}
> +	}
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		pr_info("TX-TDM Interleaved Data-\n");
> +		for (i =3D 0; i < 64; i++)
> +			pr_info("%x", output_tdm_buffer[i]);
> +		pr_info("\n");
> +	  }
> +#endif
> +	return TDM_E_OK;
> +}
> +
> +/* Channel Level APIs of TDM Framework */ int tdm_channel_open(u16
> +chanid, u16 ch_width, struct tdm_port *port,
> +				void **h_channel)
> +{
> +	struct tdm_channel *channel, *temp;
> +	unsigned long		flags;
> +	struct tdm_ch_data	*p_ch_data;
> +	int res =3D TDM_E_OK;
> +
> +	if (!(port && h_channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (ch_width !=3D 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +		if (channel->ch_id =3D=3D chanid) {
> +			pr_err("%s: Channel %d already open\n",
> +						__func__, chanid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	channel =3D kzalloc(sizeof(*channel), GFP_KERNEL);
> +	if (!channel) {
> +		res =3D -ENOMEM;
> +		goto out;
> +	}
> +
> +	p_ch_data =3D kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
> +	if (!p_ch_data) {
> +		res =3D -ENOMEM;
> +		goto outdata;
> +	}
> +
> +	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap =3D 1;
> +	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap =3D 1;
> +
> +	p_ch_data->rx_in_data =3D p_ch_data->rx_data_fifo;
> +	p_ch_data->rx_out_data =3D p_ch_data->rx_data_fifo;
> +	p_ch_data->tx_in_data =3D p_ch_data->tx_data_fifo;
> +	p_ch_data->tx_out_data =3D p_ch_data->tx_data_fifo;
> +	spin_lock_init(&p_ch_data->rx_channel_lock);
> +	spin_lock_init(&p_ch_data->tx_channel_lock);
> +
> +	channel->p_ch_data =3D p_ch_data;
> +
> +	channel->ch_id =3D chanid;
> +	channel->ch_cfg.first_slot =3D chanid;
> +	channel->ch_cfg.num_slots =3D 1;	/* This is 1 for channelized
> mode and
> +						configurable for other modes */
> +	channel->port =3D port;
> +	channel->in_use =3D 1;
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_add_tail(&channel->list, &port->mychannels);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +	*h_channel =3D channel;
> +
> +	return res;
> +
> +outdata:
> +	kfree(channel);
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_open);
> +
> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res =3D TDM_E_OK;
> +	channel =3D h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res =3D -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width !=3D 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res =3D -EINVAL;
> +		goto out;
> +	}
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_del(&channel->list);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +out:
> +	if (channel)
> +		kfree(channel->p_ch_data);
> +	kfree(channel);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_close);
> +
> +void init_config_adapter(struct tdm_adapter *adap) {
> +	struct fsl_tdm_adapt_cfg default_adapt_cfg =3D {
> +		.loopback =3D e_TDM_PROCESS_NORMAL,
> +		.num_ch =3D NUM_CHANNELS,
> +		.ch_size_type =3D CHANNEL_16BIT_LIN,
> +		.frame_len =3D NUM_SAMPLES_PER_FRAME,
> +		.num_frames =3D NUM_SAMPLES_PER_FRAME,
> +		.adap_mode =3D e_TDM_ADAPTER_MODE_NONE
> +			 };
> +
> +	default_adapt_cfg.slot_width =3D default_adapt_cfg.ch_size_type/3 +
> 1;
> +
> +	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
> +		sizeof(struct fsl_tdm_adapt_cfg));
> +
> +	return;
> +}
> +EXPORT_SYMBOL(init_config_adapter);
> +
> +static void tdm_data_tasklet_fn(unsigned long data) {
> +	struct tdm_adapter *adapter;
> +	adapter =3D (struct tdm_adapter *)data;
> +	if (adapter !=3D NULL) {
> +		tdm_data_tx_interleave(adapter);
> +		tdm_data_rx_deinterleave(adapter);
> +	}
> +}
> +
> +
> +MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
> +	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
> +MODULE_DESCRIPTION("TDM Driver Framework Core"); MODULE_LICENSE("GPL");
> diff --git a/include/linux/mod_devicetable.h
> b/include/linux/mod_devicetable.h index ae28e93..dc1a655 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -416,6 +416,17 @@ struct i2c_device_id {
>  			__attribute__((aligned(sizeof(kernel_ulong_t))));
>  };
>=20
> +/* tdm */
> +
> +#define TDM_NAME_SIZE   20
> +#define TDM_MODULE_PREFIX "tdm:"
> +
> +struct tdm_device_id {
> +	char name[TDM_NAME_SIZE];
> +	kernel_ulong_t driver_data      /* Data private to the driver */
> +			__attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
> +
>  /* spi */
>=20
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/tdm.h b/include/linux/tdm.h new file mode
> 100644 index 0000000..8cf4ef5
> --- /dev/null
> +++ b/include/linux/tdm.h
> @@ -0,0 +1,347 @@
> +/* include/linux/tdm.h
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * tdm.h - definitions for the tdm-device framework interface
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * This program 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 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.
> + *
> + * 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.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /*
> Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3
> +
> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment
> +*/ static inline int ALIGN_SIZE(u64 size, u32 alignment) {
> +	return (size + alignment - 1) & (~(alignment - 1)); }
> +
> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)  */ struct
> +tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific
> functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);
> +
> +	const struct tdm_device_id *id_table;
> +
> +	/* The associated adapter for this driver */
> +	struct tdm_adapter *adapter;
> +	struct list_head list;
> +};
> +
> +/* tdm per port statistics structure, used for providing and storing
> +tdm port
> + * statistics.
> + */
> +struct tdm_port_stats {
> +	unsigned int rx_pkt_count;	/* Rx frame count per channel */
> +	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
> +					 clean space for new buffer */
> +	unsigned int tx_pkt_count;	/* Tx frame count per channel */
> +	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count
> per
> +					 channel */
> +	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel
> due to
> +					 queue full */
> +};
> +
> +
> +/* tdm Buffer Descriptor, used for Creating Interleaved and
> +De-interleaved
> + * FIFOs
> + */
> +struct tdm_bd {
> +	unsigned char flag;		/* BD is full or empty */
> +	unsigned char wrap;		/* BD is last in the queue */
> +	unsigned short length;	/* Length of Data in BD */
> +	/*TODO: use dyanmic memory */
> +	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
> +	unsigned long offset;	/* Offset of the Data Pointer to be used */
> +};
> +
> +#define TDM_CH_RX_BD_RING_SIZE	3
> +#define TDM_CH_TX_BD_RING_SIZE	3
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_port_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel
> Data
> +								BD Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled
> by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
> +							read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel
> Data
> +								BD Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
> +						 filled by App */
> +	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read
> by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
> +};
> +
> +/* structure tdm_port_cfg - contains configuration params for a port */
> +struct tdm_port_cfg {
> +	unsigned short port_mode;
> +};
> +
> +/* struct tdm_port - represent an TDM ports for a device */ struct
> +tdm_port {
> +	unsigned short port_id;
> +	unsigned short in_use;		/* Port is enabled? */
> +	uint16_t rx_max_frames;		/* Received Port frames
> +					 before allowing Read Operation in
> +					 Port Mode */
> +
> +	struct tdm_port_stats port_stat;/* A structure parameters defining
> +					 TDM port statistics. */
> +	struct tdm_port_data *p_port_data;	/* a structure parameters
> +						defining tdm channelised data */
> +	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data
> */
> +
> +	struct tdm_driver *driver;	/* driver for this port */
> +	struct tdm_adapter *adapter;	/* adapter for this port */
> +	struct list_head list;		/* list of ports */
> +	struct list_head mychannels;	/* list of channels, created on this
> +					 port*/
> +	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
> +	struct tdm_port_cfg port_cfg;/* A structure parameters defining
> +					 TDM port configuration. */
> +};
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_ch_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data
> BD
> +								Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App
> */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data
> BD
> +								Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
> +						App */
> +	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
> +};
> +
> +/* Channel config params */
> +struct tdm_ch_cfg {
> +	unsigned short num_slots;
> +	unsigned short first_slot;
> +};
> +
> +/* struct tdm_channel- represent a TDM channel for a port */ struct
> +tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for
> channel */
> +};
> +
> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *); };
> +
> +/* tdm_adapter_mode is to define in mode of the device */ enum
> +tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE =3D 0x00,
> +	e_TDM_ADAPTER_MODE_T1 =3D 0x01,
> +	e_TDM_ADAPTER_MODE_E1 =3D 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW =3D 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW =3D 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to
> +operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED =3D 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL =3D 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL =3D 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or
> +internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL =3D 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB =3D 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB =3D 2	/* External Loopback mode */
> +};
> +
> +
> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */
> +	u32 num_frames;
> +	u8 loopback;			/* loopback or normal */
> +	u8 adap_mode;			/* 0=3DNone, 1=3D T1, 2=3D T1-FULL, 3=3DE1,
> +						4 =3D E1-FULL */
> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */
> +	int max_timeslots;		/* Max Number of timeslots that are
> +					supported on this adapter */
> +};
> +
> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device
> +along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */
> +	unsigned int class;	/* classes to allow probing for */
> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to
> access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/
> +
> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to
> perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;
> +
> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;
> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> +	void *data;
> +	struct fsl_tdm_adapt_cfg adapt_cfg;
> +};
> +
> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev) {
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void
> +*data) {
> +	dev->data =3D data;
> +}
> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *); extern int
> +tdm_del_adapter(struct tdm_adapter *); extern int
> +tdm_register_driver(struct tdm_driver *); extern void
> +tdm_del_driver(struct tdm_driver *); extern void
> +tdm_unregister_driver(struct tdm_driver *); extern void
> +init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **); extern
> +unsigned int tdm_port_close(void *); extern unsigned int
> +tdm_port_ioctl(void *, unsigned int, unsigned long); extern unsigned
> +int tdm_channel_read(void *, void *, void *, u16 *); extern unsigned
> +int tdm_channel_write(void *, void * , void *, u16); extern unsigned
> +int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);
> +
> +static inline int tdm_add_driver(struct tdm_driver *driver) {
> +	return tdm_register_driver(driver);
> +}
> +
> +extern struct tdm_adapter *tdm_get_adapter(int id); extern void
> +tdm_put_adapter(struct tdm_adapter *adap);
> +
> +#endif /* __KERNEL__ */
> +
> +#define TDM_E_OK 0
> +
> +#endif /* _LINUX_TDM_H */
> --
> 1.6.5.6

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH][2/3][RFC] TDM Framework
  2012-03-10 12:57 [PATCH][2/3][RFC] TDM Framework Poonam Aggrwal
  2012-04-23  8:00 ` Aggrwal Poonam-B10812
@ 2012-04-24  0:34 ` Scott Wood
  2012-04-27  2:07   ` Aggrwal Poonam-B10812
  2012-04-25  5:49 ` Benjamin Herrenschmidt
  2 siblings, 1 reply; 6+ messages in thread
From: Scott Wood @ 2012-04-24  0:34 UTC (permalink / raw)
  To: Poonam Aggrwal; +Cc: Sandeep Singh, linuxppc-dev

On 03/10/2012 06:57 AM, Poonam Aggrwal wrote:
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index ad6c1eb..25f7f5b 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>  
> +source "drivers/tdm/Kconfig"
>  endmenu

When posting patches to this list, please ensure that they are based on
an upstream Linux kernel and not a Freescale BSP kernel.

> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going on.
> +
> +endif # TDM

Please use the normal kernel mechanisms for controlling debug messages.

> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
> new file mode 100644
> index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program 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 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.
> + *
> + * 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.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"

What is a reference to tdm_fsl.h doing in the presumably
hardware-independent tdm-core.c?

> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data = 1;

Describe when one would want to change this, and provide a better way to
change it (e.g. module parameter or sysfs knob).

/*
 * Linux kernel
 * multi-line comment style
 * is like this.
 */

> +/* this tasklet is created for each adapter instance */
> +static void tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */
> +static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> +{
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}

s/TDM_E_OK/0/g

> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;

Why can't one driver service multiple adapters?  How would multiple
drivers service one adapter?  Are there sub-devices that need individual
controlling?

> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);

Why ignore errors?

> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;

Shouldn't this be an error?

> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;

If the driver doesn't have a detach_adapter method, you skip
decrementing the count and leave the tasklet lying around?

> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}

Pointers aren't handles.  The caller should not be passing NULL, and it
would be more useful to crash and get a backtrace if it does.  It's not
realistic to check every pointer for being NULL when there's no
legitimate reason it could be NULL, and it doesn't help you if you have
some other bad value besides NULL.

> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);

This is too noisy.  You haven't even gotten a match yet.

> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number

Why are there device numbers at all?

I suspect there's a fair bit of copy and paste going on of another
subsystem's quirks (i2c?).  I don't see any mention in the copyright
block of this code having been derived from anything else, though...

> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);

Why are you dropping the lock then reacquiring it?

> +/* TDM Client Drivers Registration/De-registration Functions */
> +int tdm_register_driver(struct tdm_driver *driver)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}

Indentation.

> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM framework
> + * @driver: the driver being unregistered
> + */
> +void tdm_unregister_driver(struct tdm_driver *driver)
> +{
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */

Whitespace.

> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
> +}
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}

Don't spam the console just because the driver got loaded or unloaded
(at this point you haven't even found the hardware).

> +/* We must initialize early, because some subsystems register tdm drivers
> + * in subsys_initcall() code, but are linked (and initialized) before tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);

tdm_init doesn't do anything, so why does it need to run early?

> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)

Why is the return unsigned int?  You're returning negative numbers.

Also consider having the return be a pointer, and use PTR_ERR/ERR_PTR --
or at least put a proper type on h_port (what is the "h_"?).

> +{
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}

Either make these pr_debug (or remove them), or make the message more
specific.

> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;

When would a port have in_use be false, other than this brief window
where nothing else should be looking at it?

> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}

Broken indentation.

> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;

Unnecessary casts.

> +	if ((port && channel) == 0) { /* invalid handle*/

This is an odd construct -- how about "if (!port || !channel)"?

> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);

Shouldn't you test whether it's in use after you get the lock?

> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}

That pr_info() is inappropriate.  This driver appears to be overly
chatty in general (and with quite vague messages) -- or is this debug
stuff that will be removed in a non-RFC patch?

> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
> +{
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}

Broken indentation.  Spaces around operators.

> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}

close() seems an odd time to be checking for supported channel width,
especially since you don't use that variable anywhere else in this function.

> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__

Is this supposed to be a userspace header ever?  If TDM exposes a
userspace interface, it needs to be documented.

> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3

These need proper namespacing -- plus, should all of these really be
hardcoded like this?  Is this standard stuff that will always be the same?

> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment */
> +static inline int ALIGN_SIZE(u64 size, u32 alignment)
> +{
> +	return (size + alignment - 1) & (~(alignment - 1));
> +}

Use the already existing ALIGN().

> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)
> + */
> +struct tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);

Please describe what semantics you expect for the non-standard functions.

Where are the "ioctl like commands" defined?  When would they be used?
I don't see it used in this patchset.

> +/* struct tdm_channel- represent a TDM channel for a port */
> +struct tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
> +};

Why are ch_id and especially in_use u16?

> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *);
> +};
> 

Provide parameter names and document the semantics you're expecting.

> +/* tdm_adapter_mode is to define in mode of the device */
> +enum tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};

Commas go at the end of lines, not the beginning.

No hungarian notation -- drop the "e_".

> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */

Is u8 really appropriate here?  Maybe int?

What does "type" mean in "ch_size_type"?

> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */

Use #defines or an enum.

> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */

If it's not used, why is it here?

> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */

What does the id mean?  Why do we need an arbitrary numberspace?

> +	unsigned int class;	/* classes to allow probing for */

What sort of values would go here?

> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/

Why is the parent device not used?

> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;

What does "tdm_rx_flag" indicate?  What about "tasklet_conf"?

> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;

If you've got more than one list, it's probably a bad idea for any of
them to be simply called "list".

> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */

Comments should add new information, not just restate what the code
already said.

> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
> +{
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
> +{
> +	dev->data = data;
> +}

Is this really needed?

> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *);
> +extern int tdm_del_adapter(struct tdm_adapter *);
> +extern int tdm_register_driver(struct tdm_driver *);
> +extern void tdm_del_driver(struct tdm_driver *);
> +extern void tdm_unregister_driver(struct tdm_driver *);
> +extern void init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> +extern unsigned int tdm_port_close(void *);
> +extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
> +extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
> +extern unsigned int tdm_channel_write(void *, void * , void *, u16);
> +extern unsigned int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);

Please provide parameter names with prototypes.

The "extern" is unnecessary.

> +static inline int tdm_add_driver(struct tdm_driver *driver)
> +{
> +	return tdm_register_driver(driver);
> +}

What is the semantic difference between tdm_add_driver() and
tdm_register_driver()?  Why do they both exist?

-Scott

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH][2/3][RFC] TDM Framework
  2012-03-10 12:57 [PATCH][2/3][RFC] TDM Framework Poonam Aggrwal
  2012-04-23  8:00 ` Aggrwal Poonam-B10812
  2012-04-24  0:34 ` Scott Wood
@ 2012-04-25  5:49 ` Benjamin Herrenschmidt
  2012-04-26 11:20   ` Singh Sandeep-B37400
  2 siblings, 1 reply; 6+ messages in thread
From: Benjamin Herrenschmidt @ 2012-04-25  5:49 UTC (permalink / raw)
  To: Poonam Aggrwal; +Cc: Sandeep Singh, linuxppc-dev

On Sat, 2012-03-10 at 18:27 +0530, Poonam Aggrwal wrote:
> From: Sandeep Singh <Sandeep@freescale.com>
> 
> TDM Framework is an attempt to provide a platform independent layer which
> can offer a standard interface  for TDM access to different client modules.
> Beneath, the framework layer can house different types of TDM drivers to handle
> various TDM devices, the hardware intricacies of the devices being completely
> taken care by TDM drivers.

Neither the changeset comment, the code, not the Documentation file
(which are non-existent, at least in this patch, though mentioned),
define what "TDM" actually is :-)

Cheers,
Ben.


> This framework layer will allow any type of TDM device to hook with it.
> For example Freescale controller as on MPC8315, UCC based TDM controller, or 
> any other controller.
> 
> The main functions of this Framework are:
> - provides interface to TDM clients to access TDM functionalities.
> - provides standard interface for TDM drivers to hook with the framework. 
> - handles various data handling stuff and buffer management.
> 
> In future this Framework will be extended to provide Interface for Line control
> devices also. For example SLIC, E1/T1 Framers etc.
> 
> Limitations/Future Work
> ---------------------------
> 1. Presently the framework supports only Single Port channelised mode.
> 2. Also the configurability options are limited which will be extended later on.
> 3. Only kernel mode TDM clients are supported currently. Support for User mode
> clients will be added later. 
> 
> Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
> Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
> ---
>  A couple of todos' are left in the patch, we are working on it and will be
> addressed in the updated patch set.
>  drivers/Kconfig                 |    1 +
>  drivers/Makefile                |    1 +
>  drivers/tdm/Kconfig             |   25 +
>  drivers/tdm/tdm-core.c          | 1146 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mod_devicetable.h |   11 +
>  include/linux/tdm.h             |  347 ++++++++++++
>  6 files changed, 1531 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/tdm/Kconfig
>  create mode 100644 drivers/tdm/tdm-core.c
>  create mode 100644 include/linux/tdm.h
> 
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index ad6c1eb..25f7f5b 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/net/dpa/NetCommSw/Kconfig"
>  
> +source "drivers/tdm/Kconfig"
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index cd546eb..362b5ed 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_INFINIBAND)	+= infiniband/
>  obj-$(CONFIG_SGI_SN)		+= sn/
>  obj-y				+= firmware/
>  obj-$(CONFIG_CRYPTO)		+= crypto/
> +obj-$(CONFIG_TDM)		+= tdm/
>  obj-$(CONFIG_SUPERH)		+= sh/
>  obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
>  ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
> diff --git a/drivers/tdm/Kconfig b/drivers/tdm/Kconfig
> new file mode 100644
> index 0000000..8db2b05
> --- /dev/null
> +++ b/drivers/tdm/Kconfig
> @@ -0,0 +1,25 @@
> +#
> +# TDM subsystem configuration
> +#
> +
> +menuconfig TDM
> +	tristate "TDM support"
> +	---help---
> +	  More information is contained in the directory <file:Documentation/tdm/>,
> +	  especially in the file called "summary" there.
> +	  If you want TDM support, you should say Y here and also to the
> +	  specific driver for your bus adapter(s) below.
> +
> +	  This TDM support can also be built as a module.  If so, the module
> +	  will be called tdm-core.
> +
> +if TDM
> +
> +config TDM_DEBUG_CORE
> +	bool "TDM Core debugging messages"
> +	help
> +	  Say Y here if you want the TDM core to produce a bunch of debug
> +	  messages to the system log.  Select this if you are having a
> +	  problem with TDM support and want to see more of what is going on.
> +
> +endif # TDM
> diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c
> new file mode 100644
> index 0000000..cdda260
> --- /dev/null
> +++ b/drivers/tdm/tdm-core.c
> @@ -0,0 +1,1146 @@
> +/* driver/tdm/tdm-core.c
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * TDM core is the interface between TDM clients and TDM devices.
> + * It is also intended to serve as an interface for line controld
> + * devices later on.
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> + * 1. Added framework based initialization of device.
> + * 2. All the init/run time configuration is now done by framework.
> + * 3. Added channel level operations.
> + *
> + * This program 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 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.
> + *
> + * 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.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/* if read write debug required */
> +#undef TDM_CORE_DEBUG
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/tdm.h>
> +#include <linux/init.h>
> +#include <linux/idr.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/hardirq.h>
> +#include <linux/irqflags.h>
> +#include <linux/list.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +#include "device/tdm_fsl.h"
> +
> +
> +static DEFINE_MUTEX(tdm_core_lock);
> +static DEFINE_IDR(tdm_adapter_idr);
> +/* List of TDM adapters registered with TDM framework */
> +LIST_HEAD(adapter_list);
> +
> +/* List of TDM clients registered with TDM framework */
> +LIST_HEAD(driver_list);
> +
> +/* In case the previous data is not fetched by the client driver, the
> + * de-interleaving function will  discard the old data and rewrite the
> + * new data */
> +static int use_latest_tdm_data = 1;
> +
> +/* this tasklet is created for each adapter instance */
> +static void tdm_data_tasklet_fn(unsigned long);
> +
> +/* tries to match client driver with the adapter */
> +static int tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> +{
> +	/* match on an id table if there is one */
> +	if (driver->id_table && driver->id_table->name[0]) {
> +		if (!(strcmp(driver->id_table->name, adap->name)))
> +			return (int)driver->id_table;
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	/* if driver is already attached to any other adapter, return*/
> +	if (driver->adapter && (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	driver->adapter = adap;
> +
> +	if (driver->attach_adapter) {
> +		if (driver->attach_adapter(adap) < 0)
> +			/* We ignore the return code; if it fails, too bad */
> +			pr_err("attach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +	adap->drv_count++;
> +
> +	if (!adap->tasklet_conf) {
> +		tasklet_init(&adap->tdm_data_tasklet, tdm_data_tasklet_fn,
> +						(unsigned long)adap);
> +		adap->tasklet_conf = 1;
> +	}
> +
> +	return TDM_E_OK;
> +}
> +
> +/* Detach client driver and adapter */
> +static int tdm_detach_driver_adap(struct tdm_driver *driver,
> +					struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +
> +	if (!driver->adapter || (driver->adapter != adap))
> +		return TDM_E_OK;
> +
> +	if (!driver->detach_adapter)
> +		return TDM_E_OK;
> +
> +	adap->drv_count--;
> +
> +	/* If no more driver is registed with the adapter*/
> +	if (!adap->drv_count && adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	if (driver->detach_adapter) {
> +		if (driver->detach_adapter(adap))
> +			pr_err("detach_adapter failed for driver [%s]\n",
> +				driver->name);
> +	}
> +
> +	driver->adapter = NULL;
> +	return res;
> +}
> +
> +/* TDM adapter Registration/De-registration with TDM framework */
> +
> +static int tdm_register_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&adap->adap_lock);
> +	INIT_LIST_HEAD(&adap->myports);
> +	spin_lock_init(&adap->portlist_lock);
> +
> +	adap->drv_count = 0;
> +	adap->tasklet_conf = 0;
> +
> +	list_add_tail(&adap->list, &adapter_list);
> +
> +	/* initialization of driver by framework in default configuration */
> +	init_config_adapter(adap);
> +
> +	/* Notify drivers */
> +	pr_info("adapter [%s] registered\n", adap->name);
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is attached with Adapter %s(ID = %d)\n",
> +				driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +
> +/*
> + * tdm_add_adapter - declare tdm adapter, use dynamic device number
> + * @adapter: the adapter to add
> + * Context: can sleep
> + *
> + * This routine is used to declare a TDM adapter
> + * When this returns zero, a new device number will be allocated and stored
> + * in adap->id, and the specified adapter became available for the clients.
> + * Otherwise, a negative errno value is returned.
> + */
> +int tdm_add_adapter(struct tdm_adapter *adapter)
> +{
> +	int id, res = TDM_E_OK;
> +	if (!adapter) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +retry:
> +	if (idr_pre_get(&tdm_adapter_idr, GFP_KERNEL) == 0)
> +		return -ENOMEM;
> +
> +	mutex_lock(&tdm_core_lock);
> +	res = idr_get_new(&tdm_adapter_idr, adapter, &id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	if (res < 0) {
> +		if (res == -EAGAIN)
> +			goto retry;
> +		return res;
> +	}
> +
> +	adapter->id = id;
> +	return tdm_register_adapter(adapter);
> +}
> +EXPORT_SYMBOL(tdm_add_adapter);
> +
> +
> +/**
> + * tdm_del_adapter - unregister TDM adapter
> + * @adap: the adapter being unregistered
> + *
> + * This unregisters an TDM adapter which was previously registered
> + * by @tdm_add_adapter.
> + */
> +int tdm_del_adapter(struct tdm_adapter *adap)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *found;
> +	struct tdm_driver *driver, *next;
> +
> +	if (!adap) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* First make sure that this adapter was ever added */
> +	mutex_lock(&tdm_core_lock);
> +	found = idr_find(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +	if (found != adap) {
> +		pr_err("tdm-core: attempting to delete unregistered "
> +			 "adapter [%s]\n", adap->name);
> +		return -EINVAL;
> +	}
> +
> +	/*disable and kill the data processing tasklet */
> +	if (adap->tasklet_conf) {
> +		tasklet_disable(&adap->tdm_data_tasklet);
> +		tasklet_kill(&adap->tdm_data_tasklet);
> +		adap->tasklet_conf = 0;
> +	}
> +
> +	/* Detach any active ports. This can't fail, thus we do not
> +	   checking the returned value. */
> +	mutex_lock(&tdm_core_lock);
> +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			tdm_detach_driver_adap(driver, adap);
> +			pr_info(
> +			"Driver(ID=%d) is detached from Adapter %s(ID = %d)\n",
> +				 driver->id, adap->name, adap->id);
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	mutex_lock(&tdm_core_lock);
> +	idr_remove(&tdm_adapter_idr, adap->id);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	pr_debug("adapter [%s] unregistered\n", adap->name);
> +
> +	list_del(&adap->list);
> +	/* Clear the device structure in case this adapter is ever going to be
> +	   added again */
> +	adap->parent = NULL;
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_del_adapter);
> +
> +/* TDM Client Drivers Registration/De-registration Functions */
> +int tdm_register_driver(struct tdm_driver *driver)
> +{
> +	int res = TDM_E_OK;
> +	struct tdm_adapter *adap, *next;
> +
> +	list_add_tail(&driver->list, &driver_list);
> +
> +	mutex_lock(&tdm_core_lock);
> +	/* Walk the adapters that are already present */
> +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> +		if (tdm_device_match(driver, adap)) {
> +			res = tdm_attach_driver_adap(driver, adap);
> +			pr_info("TDM Driver(ID=%d)is attached with Adapter"
> +				"%s(ID = %d) drv_count=%d", driver->id,
> +				adap->name, adap->id, adap->drv_count);
> +		break;
> +		}
> +	}
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_register_driver);
> +
> +/*
> + * tdm_unregister_driver - unregister TDM client driver from TDM framework
> + * @driver: the driver being unregistered
> + */
> +void tdm_unregister_driver(struct tdm_driver *driver)
> +{
> +	if (!driver) {
> +		pr_err("%s:Invalid handle\n", __func__);
> +		return;
> +	}
> +       /* A driver can register to only one adapter,
> +	* so no need to browse the list */
> +	mutex_lock(&tdm_core_lock);
> +	tdm_detach_driver_adap(driver, driver->adapter);
> +	mutex_unlock(&tdm_core_lock);
> +
> +	list_del(&driver->list);
> +
> +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name);
> +}
> +EXPORT_SYMBOL(tdm_unregister_driver);
> +
> +/* TDM Framework init and exit */
> +static int __init tdm_init(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return TDM_E_OK;
> +}
> +
> +static void __exit tdm_exit(void)
> +{
> +	pr_info("%s\n", __func__);
> +	return;
> +}
> +
> +/* We must initialize early, because some subsystems register tdm drivers
> + * in subsys_initcall() code, but are linked (and initialized) before tdm.
> + */
> +postcore_initcall(tdm_init);
> +module_exit(tdm_exit);
> +
> +
> +/* Interface to the tdm device/adapter */
> +
> +/* tdm_adap_send - issue a TDM write
> + * @adap: Handle to TDM device
> + * @buf: Data that will be written to the TDM device
> + * @count: How many bytes to write
> + *
> + * Returns negative errno, or else the number of bytes written.
> + */
> +int tdm_adap_send(struct tdm_adapter *adap, void **buf, int count)
> +{
> +	int res;
> +
> +	if ((adap == NULL) || (buf == NULL)) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (adap->algo->tdm_write)
> +		res = adap->algo->tdm_write(adap, buf, count);
> +	else {
> +		pr_err("TDM level write not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* If everything went ok (i.e. frame transmitted), return #bytes
> +	   transmitted, else error code. */
> +	return (res == 1) ? count : res;
> +}
> +EXPORT_SYMBOL(tdm_adap_send);
> +
> +/**
> + * tdm_adap_recv - issue a TDM read
> + * @adap: Handle to TDM device
> + * @buf: Where to store data read from TDM device
> + *
> + * Returns negative errno, or else the number of bytes read.
> + */
> +int tdm_adap_recv(struct tdm_adapter *adap, void **buf)
> +{
> +	int res;
> +
> +	if (adap->algo->tdm_read)
> +		res = adap->algo->tdm_read(adap, (u16 **)buf);
> +	else {
> +		pr_err("TDM level read not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. frame received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +
> +/**
> + * tdm_adap_get_write_buf - get next write TDM device buffer
> + * @adap: Handle to TDM device
> + * @buf: pointer to TDM device buffer
> + *
> + * Returns negative errno, or else size of the write buffer.
> + */
> +int tdm_adap_get_write_buf(struct tdm_adapter *adap, void **buf)
> +{
> +	int res;
> +
> +	if (adap->algo->tdm_get_write_buf) {
> +		res = adap->algo->tdm_get_write_buf(adap, (u16 **)buf);
> +	} else {
> +		pr_err("TDM level write buf get not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	/* If everything went ok (i.e. 1 msg received), return #bytes
> +	   transmitted, else error code. */
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_get_write_buf);
> +
> +int tdm_adap_enable(struct tdm_driver *drv)
> +{
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_enable) {
> +		res = adap->algo->tdm_enable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_enable);
> +
> +int tdm_adap_disable(struct tdm_driver *drv)
> +{
> +	int res;
> +	struct tdm_adapter *adap;
> +	if (drv == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	adap = drv->adapter;
> +
> +	if (adap->algo->tdm_disable) {
> +		res = adap->algo->tdm_disable(adap);
> +	} else {
> +		pr_err("TDM level enable not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_adap_disable);
> +
> +struct tdm_adapter *tdm_get_adapter(int id)
> +{
> +	struct tdm_adapter *adapter;
> +
> +	mutex_lock(&tdm_core_lock);
> +	adapter = idr_find(&tdm_adapter_idr, id);
> +	if (adapter && !try_module_get(adapter->owner))
> +		adapter = NULL;
> +
> +	mutex_unlock(&tdm_core_lock);
> +
> +	return adapter;
> +}
> +EXPORT_SYMBOL(tdm_get_adapter);
> +
> +void tdm_put_adapter(struct tdm_adapter *adap)
> +{
> +	module_put(adap->owner);
> +}
> +EXPORT_SYMBOL(tdm_put_adapter);
> +
> +
> +/* Port Level APIs of TDM Framework */
> +unsigned int tdm_port_open(struct tdm_driver *driver, void **h_port)
> +{
> +	struct tdm_port *port;
> +	struct tdm_adapter *adap;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		return -ENODEV;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	adap = tdm_get_adapter(driver->adapter->id);
> +	if (!adap)
> +		return -ENODEV;
> +
> +	/* This creates an anonymous tdm_port, which may later be
> +	 * pointed to some slot.
> +	 *
> +	 */
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);
> +	if (!port) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	init_waitqueue_head(&port->ch_wait_queue);
> +
> +
> +	port->rx_max_frames = NUM_SAMPLES_PER_FRAME;
> +	port->port_cfg.port_mode = e_TDM_PORT_CHANNELIZED;
> +
> +	port->in_use = 1;
> +
> +	snprintf(driver->name, TDM_NAME_SIZE, "tdm-dev");
> +	port->driver = driver;
> +	port->adapter = adap;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_add_tail(&port->list, &adap->myports);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	INIT_LIST_HEAD(&port->mychannels);
> +
> +	*h_port = port;
> +
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_open);
> +
> +unsigned int tdm_port_close(void *h_port)
> +{
> +	struct tdm_adapter *adap;
> +	struct tdm_driver *driver;
> +	struct tdm_port *port;
> +	struct tdm_channel *temp, *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	driver =  port->driver;
> +
> +	if (driver == NULL) {
> +		pr_err("driver NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +	if (driver->adapter == NULL) {
> +		pr_err("adapter NULL\n");
> +		res = -ENODEV;
> +		goto out;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +	if (channel)
> +		if (channel->in_use) {
> +			pr_err("%s: Cannot close port. Channel in use\n",
> +								__func__);
> +			res = -ENXIO;
> +			goto out;
> +			}
> +	}
> +	adap = driver->adapter;
> +
> +	spin_lock_irqsave(&adap->portlist_lock, flags);
> +	list_del(&port->list);
> +	spin_unlock_irqrestore(&adap->portlist_lock, flags);
> +
> +	if (port->p_port_data != NULL) {
> +		int i;
> +		struct tdm_bd *ch_bd;
> +
> +		/* If the tdm is in channelised mode,
> +		de-allocate the channelised buffer */
> +		ch_bd = &(port->p_port_data->rx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_RX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		ch_bd = &(port->p_port_data->tx_data_fifo[0]);
> +		for (i = 0; ch_bd && i < TDM_CH_TX_BD_RING_SIZE; i++) {
> +			ch_bd->flag = 0;
> +			ch_bd++;
> +		}
> +		kfree(port->p_port_data);
> +	}
> +	kfree(port);
> +	return res;
> +out:
> +	if (port)
> +		kfree(port->p_port_data);
> +	kfree(port);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_port_close);
> +
> +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> +				void *p_data, u16 *size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *rx_bd;
> +	unsigned long flags;
> +	int i, res = TDM_E_OK;
> +	unsigned short *buf, *buf1;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("%s:Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
> +	rx_bd = channel->p_ch_data->rx_out_data;
> +
> +	if (rx_bd->flag) {
> +		*size = rx_bd->length;
> +		buf = (u16 *) p_data;
> +		buf1 = (u16 *)rx_bd->p_data;
> +		for (i = 0; i < NUM_SAMPLES_PER_FRAME; i++)
> +			buf[i] = buf1[i];
> +		rx_bd->flag = 0;
> +		rx_bd->offset = 0;
> +		channel->p_ch_data->rx_out_data = (rx_bd->wrap) ?
> +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> +
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> +						flags);
> +		pr_info("No Data Available");
> +		return -EAGAIN;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock, flags);
> +
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_read);
> +
> +
> +unsigned int tdm_channel_write(void *h_port, void *h_channel,
> +				void *p_data, u16 size)
> +{
> +	struct tdm_port *port;
> +	struct tdm_channel *channel;
> +	struct tdm_bd *tx_bd;
> +	unsigned long flags;
> +	int err = TDM_E_OK;
> +	port = (struct tdm_port *)h_port;
> +	channel = (struct tdm_channel *)h_channel;
> +#ifdef TDM_CORE_DEBUG
> +	bool data_flag = 0;
> +#endif
> +
> +	if ((port && channel) == 0) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +
> +	if (p_data == NULL) { /* invalid data*/
> +		pr_err("Invalid Data");
> +		return -EFAULT;
> +	}
> +
> +	if (!port->in_use)
> +		return -EIO;
> +	if (!channel->p_ch_data || !channel->in_use)
> +		return -EIO;
> +
> +	spin_lock_irqsave(&channel->p_ch_data->tx_channel_lock, flags);
> +	tx_bd = channel->p_ch_data->tx_in_data;
> +
> +	if (!tx_bd->flag) {
> +		tx_bd->length = size;
> +		memcpy(tx_bd->p_data, p_data,
> +			size * port->adapter->adapt_cfg.slot_width);
> +		tx_bd->flag = 1;
> +		tx_bd->offset = 0;
> +		channel->p_ch_data->tx_in_data = (tx_bd->wrap) ?
> +				channel->p_ch_data->tx_data_fifo : tx_bd+1;
> +		port->port_stat.tx_pkt_count++;
> +#ifdef TDM_CORE_DEBUG
> +		data_flag = 1;
> +#endif
> +	} else {
> +		spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock,
> +						flags);
> +		port->port_stat.tx_pkt_drop_count++;
> +		pr_err("E_NO_MEMORY -Failed Transmit");
> +		return -ENOMEM;
> +	}
> +	spin_unlock_irqrestore(&channel->p_ch_data->tx_channel_lock, flags);
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		int k;
> +		pr_info("\nTX port:%d - Write - Port TX-%d\n",
> +						port->port_id, size);
> +		for (k = 0; k < size; k++)
> +			pr_info("%x", p_data[k]);
> +		pr_info("\n");
> +	}
> +#endif
> +	return err;
> +}
> +EXPORT_SYMBOL(tdm_channel_write);
> +
> +wait_queue_head_t *tdm_port_get_wait_queue(void  *h_port)
> +{
> +	struct tdm_port *port;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return NULL;
> +	}
> +
> +	return &port->ch_wait_queue;
> +
> +}
> +EXPORT_SYMBOL(tdm_port_get_wait_queue);
> +
> +/* Driver Function for select and poll. Based on Port no, it sleeps on
> + * waitqueue */
> +unsigned int tdm_port_poll(void *h_port, unsigned int wait_time)
> +{
> +	struct tdm_port *port;
> +	unsigned long timeout = msecs_to_jiffies(wait_time);
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +	if (!port->p_port_data || !port->in_use)
> +		return -EIO;
> +
> +	if (port->p_port_data->rx_out_data->flag) {
> +		pr_debug("Data Available");
> +		return TDM_E_OK;
> +	}
> +	if (timeout) {
> +		wait_event_interruptible_timeout(port->ch_wait_queue,
> +					  port->p_port_data->rx_out_data->flag,
> +					  timeout);
> +
> +		if (port->p_port_data->rx_out_data->flag) {
> +			pr_debug("Data Available");
> +			return TDM_E_OK;
> +		}
> +	}
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(tdm_port_poll);
> +
> +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats *portStat)
> +{
> +	struct tdm_port *port;
> +	int port_num;
> +	port = (struct tdm_port *)h_port;
> +
> +	if (port == NULL || portStat == NULL) { /* invalid handle*/
> +		pr_err("Invalid Handle");
> +		return -ENXIO;
> +	}
> +	port_num =  port->port_id;
> +
> +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> +
> +	pr_info("TDM Port %d Get Stats", port_num);
> +
> +	return TDM_E_OK;
> +}
> +EXPORT_SYMBOL(tdm_port_get_stats);
> +
> +/* Data handling functions */
> +
> +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +
> +	int i, buf_size, ch_data_len;
> +	u16 *input_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int slot_width;
> +	int frame_ch_data_size;
> +	bool ch_data;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +	ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	ch_data = 0;
> +
> +	if (!adap) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	slot_width = adap->adapt_cfg.slot_width;
> +	buf_size = tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> +	if (buf_size <= 0 || !input_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/slot_width;
> +
> +	/* de-interleaving for all ports*/
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* if the port is not open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +							list) {
> +		/* if the channel is not open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +		ch_bd = channel->p_ch_data->rx_in_data;
> +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> +			/*if old data is to be discarded */
> +		if (use_latest_tdm_data)
> +			if (ch_bd->flag) {
> +				ch_bd->flag = 0;
> +				ch_bd->offset = 0;
> +				if (ch_bd == channel->p_ch_data->rx_out_data)
> +					channel->p_ch_data->rx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.rx_pkt_drop_count++;
> +				}
> +			/* if the bd is empty */
> +			if (!ch_bd->flag) {
> +				if (ch_bd->offset == 0)
> +					ch_bd->length = port->rx_max_frames;
> +
> +				pcm_buffer = ch_bd->p_data + ch_bd->offset;
> +				/* De-interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					pcm_buffer[i]
> +					= input_tdm_buffer[i*bytes_slot_offset +
> +						channel->ch_id];
> +				}
> +				ch_bd->offset += ch_data_len * slot_width;
> +
> +				if (ch_bd->offset >=
> +					(ch_bd->length - frame_ch_data_size)*
> +						(adap->adapt_cfg.slot_width)) {
> +					ch_bd->flag = 1;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->rx_in_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->rx_data_fifo
> +						: ch_bd+1;
> +					ch_data = 1;
> +				}
> +			} else {
> +				port->port_stat.rx_pkt_drop_count++;
> +			}
> +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> +		}
> +
> +		if (ch_data) {
> +			/*	Wake up the Port Data Poll event */
> +			wake_up_interruptible(&port->ch_wait_queue);
> +#ifdef	TDM_CORE_DEBUG
> +			pr_info("Port RX-%d-%d\n", channel->ch_id, ch_data_len);
> +			for (i = 0; i < ch_data_len; i++)
> +				pr_info("%x", pcm_buffer[i]);
> +			pr_info("\n");
> +#endif
> +			port->port_stat.rx_pkt_count++;
> +			ch_data = 0;
> +		}
> +	}
> +	return TDM_E_OK;
> +}
> +
> +static int tdm_data_tx_interleave(struct tdm_adapter *adap)
> +{
> +	struct tdm_port *port, *next;
> +	struct tdm_channel *channel, *temp;
> +	struct tdm_bd	*ch_bd;
> +	int i, buf_size, ch_data_len = NUM_SAMPLES_PER_FRAME;
> +	bool last_data = 0;
> +	u16 *output_tdm_buffer;
> +	u16 *pcm_buffer;
> +	int frame_ch_data_size = NUM_SAMPLES_PER_FRAME;
> +	int bytes_in_fifo_per_frame;
> +	int bytes_slot_offset;
> +
> +#ifdef TDM_CORE_DEBUG
> +	u8	data_flag = 0;
> +#endif
> +
> +	if (adap == NULL) { /* invalid handle*/
> +		pr_err("%s: Invalid Handle\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	buf_size = tdm_adap_get_write_buf(adap, (void **)&output_tdm_buffer);
> +	if (buf_size <= 0 || !output_tdm_buffer)
> +		return -EINVAL;
> +
> +	bytes_in_fifo_per_frame = buf_size/frame_ch_data_size;
> +	bytes_slot_offset = bytes_in_fifo_per_frame/adap->adapt_cfg.slot_width;
> +
> +
> +	memset(output_tdm_buffer, 0, sizeof(buf_size));
> +
> +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> +
> +		/* check if the port is open */
> +		if (!port->in_use)
> +			continue;
> +
> +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> +								list) {
> +		pr_debug("TX-Tdm %d (slots-)", channel->ch_id);
> +
> +
> +		/* if the channel is open */
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +
> +		spin_lock(&channel->p_ch_data->tx_channel_lock);
> +		if (!channel->in_use || !channel->p_ch_data)
> +			continue;
> +			ch_bd = channel->p_ch_data->tx_out_data;
> +			if (ch_bd->flag) {
> +				pcm_buffer = (u16 *)((uint8_t *)ch_bd->p_data +
> +						ch_bd->offset);
> +				/*if the buffer has less frames than required */
> +				if (frame_ch_data_size >=
> +					((ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width))) {
> +					ch_data_len =
> +					(ch_bd->length) - (ch_bd->offset/
> +						adap->adapt_cfg.slot_width);
> +					last_data = 1;
> +				} else {
> +					ch_data_len = frame_ch_data_size;
> +				}
> +				/* Interleaving the data */
> +				for (i = 0; i < ch_data_len; i++) {
> +					/* TODO- need to be generic for any size
> +					   assignment*/
> +					output_tdm_buffer[channel->ch_id +
> +						bytes_slot_offset * i] =
> +								pcm_buffer[i];
> +				}
> +				/* If all the data of this buffer is
> +							transmitted */
> +				if (last_data) {
> +					ch_bd->flag = 0;
> +					ch_bd->offset = 0;
> +					channel->p_ch_data->tx_out_data =
> +						ch_bd->wrap ?
> +						channel->p_ch_data->tx_data_fifo
> +						: ch_bd+1;
> +					port->port_stat.tx_pkt_conf_count++;
> +				} else {
> +					ch_bd->offset += ch_data_len *
> +						(adap->adapt_cfg.slot_width);
> +				}
> +#ifdef	TDM_CORE_DEBUG
> +				data_flag = 1;
> +#endif
> +			}
> +		spin_unlock(&channel->p_ch_data->tx_channel_lock);
> +		}
> +	}
> +
> +#ifdef	TDM_CORE_DEBUG
> +	if (data_flag) {
> +		pr_info("TX-TDM Interleaved Data-\n");
> +		for (i = 0; i < 64; i++)
> +			pr_info("%x", output_tdm_buffer[i]);
> +		pr_info("\n");
> +	  }
> +#endif
> +	return TDM_E_OK;
> +}
> +
> +/* Channel Level APIs of TDM Framework */
> +int tdm_channel_open(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				void **h_channel)
> +{
> +	struct tdm_channel *channel, *temp;
> +	unsigned long		flags;
> +	struct tdm_ch_data	*p_ch_data;
> +	int res = TDM_E_OK;
> +
> +	if (!(port && h_channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> +		if (channel->ch_id == chanid) {
> +			pr_err("%s: Channel %d already open\n",
> +						__func__, chanid);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	channel = kzalloc(sizeof(*channel), GFP_KERNEL);
> +	if (!channel) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +
> +	p_ch_data = kzalloc(sizeof(struct tdm_port_data), GFP_KERNEL);
> +	if (!p_ch_data) {
> +		res = -ENOMEM;
> +		goto outdata;
> +	}
> +
> +	p_ch_data->rx_data_fifo[TDM_CH_RX_BD_RING_SIZE-1].wrap = 1;
> +	p_ch_data->tx_data_fifo[TDM_CH_TX_BD_RING_SIZE-1].wrap = 1;
> +
> +	p_ch_data->rx_in_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->rx_out_data = p_ch_data->rx_data_fifo;
> +	p_ch_data->tx_in_data = p_ch_data->tx_data_fifo;
> +	p_ch_data->tx_out_data = p_ch_data->tx_data_fifo;
> +	spin_lock_init(&p_ch_data->rx_channel_lock);
> +	spin_lock_init(&p_ch_data->tx_channel_lock);
> +
> +	channel->p_ch_data = p_ch_data;
> +
> +	channel->ch_id = chanid;
> +	channel->ch_cfg.first_slot = chanid;
> +	channel->ch_cfg.num_slots = 1;	/* This is 1 for channelized mode and
> +						configurable for other modes */
> +	channel->port = port;
> +	channel->in_use = 1;
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_add_tail(&channel->list, &port->mychannels);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +	*h_channel = channel;
> +
> +	return res;
> +
> +outdata:
> +	kfree(channel);
> +out:
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_open);
> +
> +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> +				struct tdm_channel *h_channel)
> +{
> +	struct tdm_channel *channel;
> +	unsigned long		flags;
> +	int res = TDM_E_OK;
> +	channel = h_channel;
> +
> +	if (!(port && channel)) {
> +		pr_err("%s: Invalid handle\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ch_width != 1) {
> +		pr_err("%s: Mode not supported\n", __func__);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +
> +	spin_lock_irqsave(&port->ch_list_lock, flags);
> +	list_del(&channel->list);
> +	spin_unlock_irqrestore(&port->ch_list_lock, flags);
> +
> +out:
> +	if (channel)
> +		kfree(channel->p_ch_data);
> +	kfree(channel);
> +	return res;
> +}
> +EXPORT_SYMBOL(tdm_channel_close);
> +
> +void init_config_adapter(struct tdm_adapter *adap)
> +{
> +	struct fsl_tdm_adapt_cfg default_adapt_cfg = {
> +		.loopback = e_TDM_PROCESS_NORMAL,
> +		.num_ch = NUM_CHANNELS,
> +		.ch_size_type = CHANNEL_16BIT_LIN,
> +		.frame_len = NUM_SAMPLES_PER_FRAME,
> +		.num_frames = NUM_SAMPLES_PER_FRAME,
> +		.adap_mode = e_TDM_ADAPTER_MODE_NONE
> +			 };
> +
> +	default_adapt_cfg.slot_width = default_adapt_cfg.ch_size_type/3 + 1;
> +
> +	memcpy(&adap->adapt_cfg, &default_adapt_cfg,
> +		sizeof(struct fsl_tdm_adapt_cfg));
> +
> +	return;
> +}
> +EXPORT_SYMBOL(init_config_adapter);
> +
> +static void tdm_data_tasklet_fn(unsigned long data)
> +{
> +	struct tdm_adapter *adapter;
> +	adapter = (struct tdm_adapter *)data;
> +	if (adapter != NULL) {
> +		tdm_data_tx_interleave(adapter);
> +		tdm_data_rx_deinterleave(adapter);
> +	}
> +}
> +
> +
> +MODULE_AUTHOR("Hemant Agrawal <hemant@freescale.com> and "
> +	"Rajesh Gumasta <rajesh.gumasta@freescale.com>");
> +MODULE_DESCRIPTION("TDM Driver Framework Core");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index ae28e93..dc1a655 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -416,6 +416,17 @@ struct i2c_device_id {
>  			__attribute__((aligned(sizeof(kernel_ulong_t))));
>  };
>  
> +/* tdm */
> +
> +#define TDM_NAME_SIZE   20
> +#define TDM_MODULE_PREFIX "tdm:"
> +
> +struct tdm_device_id {
> +	char name[TDM_NAME_SIZE];
> +	kernel_ulong_t driver_data      /* Data private to the driver */
> +			__attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
> +
>  /* spi */
>  
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/tdm.h b/include/linux/tdm.h
> new file mode 100644
> index 0000000..8cf4ef5
> --- /dev/null
> +++ b/include/linux/tdm.h
> @@ -0,0 +1,347 @@
> +/* include/linux/tdm.h
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights reserved.
> + *
> + * tdm.h - definitions for the tdm-device framework interface
> + *
> + * Author:Hemant Agrawal <hemant@freescale.com>
> + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> + *
> + * This program 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 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.
> + *
> + * 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.,
> + * 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +
> +#ifndef _LINUX_TDM_H
> +#define _LINUX_TDM_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/device.h>	/* for struct device */
> +#include <linux/sched.h>	/* for completion */
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +
> +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> +
> +#define NUM_CHANNELS		16
> +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> +						 channel. Req for voice data */
> +#define NUM_MS			10
> +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /* Number of
> +						samples for 1 client buffer */
> +#define NUM_OF_TDM_BUF		3
> +
> +/* General options */
> +
> +struct tdm_adapt_algorithm;
> +struct tdm_adapter;
> +struct tdm_port;
> +struct tdm_driver;
> +
> +/* Align addr on a size boundary - adjust address up if needed */
> +/* returns min value greater than size which is multiple of alignment */
> +static inline int ALIGN_SIZE(u64 size, u32 alignment)
> +{
> +	return (size + alignment - 1) & (~(alignment - 1));
> +}
> +
> +/**
> + * struct tdm_driver - represent an TDM device driver
> + * @class: What kind of tdm device we instantiate (for detect)
> + * @id:Driver id
> + * @name: Name of the driver
> + * @attach_adapter: Callback for device addition (for legacy drivers)
> + * @detach_adapter: Callback for device removal (for legacy drivers)
> + * @probe: Callback for device binding
> + * @remove: Callback for device unbinding
> + * @shutdown: Callback for device shutdown
> + * @suspend: Callback for device suspend
> + * @resume: Callback for device resume
> + * @command: Callback for sending commands to device
> + * @id_table: List of TDM devices supported by this driver
> + * @list: List of drivers created (for tdm-core use only)
> + */
> +struct tdm_driver {
> +	unsigned int class;
> +	unsigned int id;
> +	char name[TDM_NAME_SIZE];
> +
> +	int (*attach_adapter)(struct tdm_adapter *);
> +	int (*detach_adapter)(struct tdm_adapter *);
> +
> +	/* Standard driver model interfaces */
> +	int (*probe)(const struct tdm_device_id *);
> +	int (*remove)(void);
> +
> +	/* driver model interfaces that don't relate to enumeration */
> +	void (*shutdown)(void);
> +	int (*suspend)(pm_message_t mesg);
> +	int (*resume)(void);
> +
> +	/* a ioctl like command that can be used to perform specific functions
> +	 * with the device.
> +	 */
> +	int (*command)(unsigned int cmd, void *arg);
> +
> +	const struct tdm_device_id *id_table;
> +
> +	/* The associated adapter for this driver */
> +	struct tdm_adapter *adapter;
> +	struct list_head list;
> +};
> +
> +/* tdm per port statistics structure, used for providing and storing tdm port
> + * statistics.
> + */
> +struct tdm_port_stats {
> +	unsigned int rx_pkt_count;	/* Rx frame count per channel */
> +	unsigned int rx_pkt_drop_count;	/* Rx drop count per channel to
> +					 clean space for new buffer */
> +	unsigned int tx_pkt_count;	/* Tx frame count per channel */
> +	unsigned int tx_pkt_conf_count;	/* Tx frame confirmation count per
> +					 channel */
> +	unsigned int tx_pkt_drop_count;	/* Tx drop count per channel due to
> +					 queue full */
> +};
> +
> +
> +/* tdm Buffer Descriptor, used for Creating Interleaved and De-interleaved
> + * FIFOs
> + */
> +struct tdm_bd {
> +	unsigned char flag;		/* BD is full or empty */
> +	unsigned char wrap;		/* BD is last in the queue */
> +	unsigned short length;	/* Length of Data in BD */
> +	/*TODO: use dyanmic memory */
> +	unsigned short p_data[NUM_SAMPLES_PER_FRAME];	/* Data Pointer */
> +	unsigned long offset;	/* Offset of the Data Pointer to be used */
> +};
> +
> +#define TDM_CH_RX_BD_RING_SIZE	3
> +#define TDM_CH_TX_BD_RING_SIZE	3
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_port_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Channel Data
> +								BD Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Channel Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data;	/* Current Channel Rx BD to be
> +							read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Channel Data
> +								BD Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Channel Tx BD to be
> +						 filled by App */
> +	struct tdm_bd *tx_out_data;	/* Current Channel Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Channel */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Channel */
> +};
> +
> +/* structure tdm_port_cfg - contains configuration params for a port */
> +struct tdm_port_cfg {
> +	unsigned short port_mode;
> +};
> +
> +/* struct tdm_port - represent an TDM ports for a device */
> +struct tdm_port {
> +	unsigned short port_id;
> +	unsigned short in_use;		/* Port is enabled? */
> +	uint16_t rx_max_frames;		/* Received Port frames
> +					 before allowing Read Operation in
> +					 Port Mode */
> +
> +	struct tdm_port_stats port_stat;/* A structure parameters defining
> +					 TDM port statistics. */
> +	struct tdm_port_data *p_port_data;	/* a structure parameters
> +						defining tdm channelised data */
> +	wait_queue_head_t ch_wait_queue;	/* waitQueue for RX Port Data */
> +
> +	struct tdm_driver *driver;	/* driver for this port */
> +	struct tdm_adapter *adapter;	/* adapter for this port */
> +	struct list_head list;		/* list of ports */
> +	struct list_head mychannels;	/* list of channels, created on this
> +					 port*/
> +	spinlock_t ch_list_lock;	/* Spin Lock for channel_list */
> +	struct tdm_port_cfg port_cfg;/* A structure parameters defining
> +					 TDM port configuration. */
> +};
> +
> +/* tdm RX-TX Channelised Data */
> +struct tdm_ch_data {
> +	struct tdm_bd rx_data_fifo[TDM_CH_RX_BD_RING_SIZE]; /* Rx Port Data BD
> +								Ring */
> +	struct tdm_bd *rx_in_data;	/* Current Port Rx BD to be filled by
> +						de-interleave function */
> +	struct tdm_bd *rx_out_data; /* Current Port Rx BD to be read by App */
> +	struct tdm_bd tx_data_fifo[TDM_CH_TX_BD_RING_SIZE]; /* Tx Port Data BD
> +								Ring */
> +	struct tdm_bd *tx_in_data;	/* Current Port Tx BD to be filled by
> +						App */
> +	struct tdm_bd *tx_out_data;	/* Current Port Tx BD to be read by
> +						interleave function */
> +	spinlock_t rx_channel_lock;	/* Spin Lock for Rx Port */
> +	spinlock_t tx_channel_lock;	/* Spin Lock for Tx Port */
> +};
> +
> +/* Channel config params */
> +struct tdm_ch_cfg {
> +	unsigned short num_slots;
> +	unsigned short first_slot;
> +};
> +
> +/* struct tdm_channel- represent a TDM channel for a port */
> +struct tdm_channel {
> +	u16 ch_id;			/* logical channel number */
> +	struct list_head list;		/* list of channels in a port*/
> +	struct tdm_port *port;		/* port for this channel */
> +	u16 in_use;			/* channel is enabled? */
> +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> +	struct tdm_ch_data *p_ch_data;	/* data storage space for channel */
> +};
> +
> +/* tdm_adapt_algorithm is for accessing the routines of device */
> +struct tdm_adapt_algorithm {
> +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> +	int (*tdm_enable)(struct tdm_adapter *);
> +	int (*tdm_disable)(struct tdm_adapter *);
> +};
> +
> +/* tdm_adapter_mode is to define in mode of the device */
> +enum tdm_adapter_mode {
> +	e_TDM_ADAPTER_MODE_NONE = 0x00,
> +	e_TDM_ADAPTER_MODE_T1 = 0x01,
> +	e_TDM_ADAPTER_MODE_E1 = 0x02,
> +	e_TDM_ADAPTER_MODE_T1_RAW = 0x10,
> +	e_TDM_ADAPTER_MODE_E1_RAW = 0x20,
> +};
> +
> +/* tdm_port_mode defines the mode in which the port is configured to operate
> + * It can be channelized/full/fractional.
> + */
> +enum tdm_port_mode {
> +	e_TDM_PORT_CHANNELIZED = 0	/* Channelized mode */
> +	, e_TDM_PORT_FULL = 1		/* Full mode */
> +	, e_TDM_PORT_FRACTIONAL = 2	/* Fractional mode */
> +};
> +
> +/* tdm_process_mode used for testing the tdm device in normal mode or internal
> + * loopback or external loopback
> + */
> +enum tdm_process_mode {
> +	e_TDM_PROCESS_NORMAL = 0	/* Normal mode */
> +	, e_TDM_PROCESS_INT_LPB = 1	/* Internal loop mode */
> +	, e_TDM_PROCESS_EXT_LPB = 2	/* External Loopback mode */
> +};
> +
> +
> +/* TDM configuration parameters */
> +struct fsl_tdm_adapt_cfg {
> +	u8 num_ch;		/* Number of channels in this adpater */
> +	u8 ch_size_type;		/* reciever/transmit channel
> +						size for all channels */
> +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> +	u8 frame_len;		/* Length of frame in samples */
> +	u32 num_frames;
> +	u8 loopback;			/* loopback or normal */
> +	u8 adap_mode;			/* 0=None, 1= T1, 2= T1-FULL, 3=E1,
> +						4 = E1-FULL */
> +	int max_num_ports;		/* Not Used: Max Number of ports that
> +					can be created on this adapter */
> +	int max_timeslots;		/* Max Number of timeslots that are
> +					supported on this adapter */
> +};
> +
> +/*
> + * tdm_adapter is the structure used to identify a physical tdm device along
> + * with the access algorithms necessary to access it.
> + */
> +struct tdm_adapter {
> +	struct module *owner;	/* owner of the adapter module */
> +	unsigned int id;	/* Adapter Id */
> +	unsigned int class;	/* classes to allow probing for */
> +	unsigned int drv_count;	/* Number of drivers associated with the
> +				 adapter */
> +
> +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to access the
> +						 adapter*/
> +
> +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> +	struct mutex adap_lock;
> +	struct device *parent;		/*Not Used*/
> +
> +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to perform
> +						 data processing*/
> +	int tasklet_conf;	/* flag for tasklet configuration */
> +	int tdm_rx_flag;
> +
> +	struct list_head myports;	/* list of ports, created on this
> +					 adapter */
> +	struct list_head list;
> +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
> +	void *data;
> +	struct fsl_tdm_adapt_cfg adapt_cfg;
> +};
> +
> +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev)
> +{
> +	return dev->data;
> +}
> +
> +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void *data)
> +{
> +	dev->data = data;
> +}
> +
> +/* functions exported by tdm.o */
> +
> +extern int tdm_add_adapter(struct tdm_adapter *);
> +extern int tdm_del_adapter(struct tdm_adapter *);
> +extern int tdm_register_driver(struct tdm_driver *);
> +extern void tdm_del_driver(struct tdm_driver *);
> +extern void tdm_unregister_driver(struct tdm_driver *);
> +extern void init_config_adapter(struct tdm_adapter *);
> +
> +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> +extern unsigned int tdm_port_close(void *);
> +extern unsigned int tdm_port_ioctl(void *, unsigned int, unsigned long);
> +extern unsigned int tdm_channel_read(void *, void *, void *, u16 *);
> +extern unsigned int tdm_channel_write(void *, void * , void *, u16);
> +extern unsigned int tdm_port_poll(void *, unsigned int);
> +
> +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> +						struct tdm_channel *);
> +
> +static inline int tdm_add_driver(struct tdm_driver *driver)
> +{
> +	return tdm_register_driver(driver);
> +}
> +
> +extern struct tdm_adapter *tdm_get_adapter(int id);
> +extern void tdm_put_adapter(struct tdm_adapter *adap);
> +
> +#endif /* __KERNEL__ */
> +
> +#define TDM_E_OK 0
> +
> +#endif /* _LINUX_TDM_H */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH][2/3][RFC] TDM Framework
  2012-04-25  5:49 ` Benjamin Herrenschmidt
@ 2012-04-26 11:20   ` Singh Sandeep-B37400
  0 siblings, 0 replies; 6+ messages in thread
From: Singh Sandeep-B37400 @ 2012-04-26 11:20 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Aggrwal Poonam-B10812; +Cc: linuxppc-dev

LS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCkZyb206IEJlbmphbWluIEhlcnJlbnNjaG1pZHQg
W21haWx0bzpiZW5oQGtlcm5lbC5jcmFzaGluZy5vcmddIA0KU2VudDogV2VkbmVzZGF5LCBBcHJp
bCAyNSwgMjAxMiAxMToyMCBBTQ0KVG86IEFnZ3J3YWwgUG9vbmFtLUIxMDgxMg0KQ2M6IGxpbnV4
cHBjLWRldkBsaXN0cy5vemxhYnMub3JnOyBTaW5naCBTYW5kZWVwLUIzNzQwMA0KU3ViamVjdDog
UmU6IFtQQVRDSF1bMi8zXVtSRkNdIFRETSBGcmFtZXdvcmsNCg0KT24gU2F0LCAyMDEyLTAzLTEw
IGF0IDE4OjI3ICswNTMwLCBQb29uYW0gQWdncndhbCB3cm90ZToNCj4gRnJvbTogU2FuZGVlcCBT
aW5naCA8U2FuZGVlcEBmcmVlc2NhbGUuY29tPg0KPiANCj4gVERNIEZyYW1ld29yayBpcyBhbiBh
dHRlbXB0IHRvIHByb3ZpZGUgYSBwbGF0Zm9ybSBpbmRlcGVuZGVudCBsYXllciANCj4gd2hpY2gg
Y2FuIG9mZmVyIGEgc3RhbmRhcmQgaW50ZXJmYWNlICBmb3IgVERNIGFjY2VzcyB0byBkaWZmZXJl
bnQgY2xpZW50IG1vZHVsZXMuDQo+IEJlbmVhdGgsIHRoZSBmcmFtZXdvcmsgbGF5ZXIgY2FuIGhv
dXNlIGRpZmZlcmVudCB0eXBlcyBvZiBURE0gZHJpdmVycyANCj4gdG8gaGFuZGxlIHZhcmlvdXMg
VERNIGRldmljZXMsIHRoZSBoYXJkd2FyZSBpbnRyaWNhY2llcyBvZiB0aGUgZGV2aWNlcyANCj4g
YmVpbmcgY29tcGxldGVseSB0YWtlbiBjYXJlIGJ5IFRETSBkcml2ZXJzLg0KDQpOZWl0aGVyIHRo
ZSBjaGFuZ2VzZXQgY29tbWVudCwgdGhlIGNvZGUsIG5vdCB0aGUgRG9jdW1lbnRhdGlvbiBmaWxl
ICh3aGljaCBhcmUgbm9uLWV4aXN0ZW50LCBhdCBsZWFzdCBpbiB0aGlzIHBhdGNoLCB0aG91Z2gg
bWVudGlvbmVkKSwgZGVmaW5lIHdoYXQgIlRETSIgYWN0dWFsbHkgaXMgOi0pDQoNCltTYW5kZWVw
XSBUaGFua3MgZm9yIHlvdXIgY29tbWVudHMuIERvY3VtZW50YXRpb24gZm9yIFRETSBpcyBwcmVz
ZW50IGluIHRoZSBmb2xsb3dpbmcgcGF0Y2g6DQoNCmh0dHA6Ly9wYXRjaHdvcmsub3psYWJzLm9y
Zy9wYXRjaC8xNDU4NTcvDQoNClJlZ2FyZHMsDQpTYW5kZWVwDQoNCkNoZWVycywNCkJlbi4NCg0K
DQo+IFRoaXMgZnJhbWV3b3JrIGxheWVyIHdpbGwgYWxsb3cgYW55IHR5cGUgb2YgVERNIGRldmlj
ZSB0byBob29rIHdpdGggaXQuDQo+IEZvciBleGFtcGxlIEZyZWVzY2FsZSBjb250cm9sbGVyIGFz
IG9uIE1QQzgzMTUsIFVDQyBiYXNlZCBURE0gDQo+IGNvbnRyb2xsZXIsIG9yIGFueSBvdGhlciBj
b250cm9sbGVyLg0KPiANCj4gVGhlIG1haW4gZnVuY3Rpb25zIG9mIHRoaXMgRnJhbWV3b3JrIGFy
ZToNCj4gLSBwcm92aWRlcyBpbnRlcmZhY2UgdG8gVERNIGNsaWVudHMgdG8gYWNjZXNzIFRETSBm
dW5jdGlvbmFsaXRpZXMuDQo+IC0gcHJvdmlkZXMgc3RhbmRhcmQgaW50ZXJmYWNlIGZvciBURE0g
ZHJpdmVycyB0byBob29rIHdpdGggdGhlIGZyYW1ld29yay4gDQo+IC0gaGFuZGxlcyB2YXJpb3Vz
IGRhdGEgaGFuZGxpbmcgc3R1ZmYgYW5kIGJ1ZmZlciBtYW5hZ2VtZW50Lg0KPiANCj4gSW4gZnV0
dXJlIHRoaXMgRnJhbWV3b3JrIHdpbGwgYmUgZXh0ZW5kZWQgdG8gcHJvdmlkZSBJbnRlcmZhY2Ug
Zm9yIA0KPiBMaW5lIGNvbnRyb2wgZGV2aWNlcyBhbHNvLiBGb3IgZXhhbXBsZSBTTElDLCBFMS9U
MSBGcmFtZXJzIGV0Yy4NCj4gDQo+IExpbWl0YXRpb25zL0Z1dHVyZSBXb3JrDQo+IC0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLQ0KPiAxLiBQcmVzZW50bHkgdGhlIGZyYW1ld29yayBzdXBwb3J0
cyBvbmx5IFNpbmdsZSBQb3J0IGNoYW5uZWxpc2VkIG1vZGUuDQo+IDIuIEFsc28gdGhlIGNvbmZp
Z3VyYWJpbGl0eSBvcHRpb25zIGFyZSBsaW1pdGVkIHdoaWNoIHdpbGwgYmUgZXh0ZW5kZWQgbGF0
ZXIgb24uDQo+IDMuIE9ubHkga2VybmVsIG1vZGUgVERNIGNsaWVudHMgYXJlIHN1cHBvcnRlZCBj
dXJyZW50bHkuIFN1cHBvcnQgZm9yIA0KPiBVc2VyIG1vZGUgY2xpZW50cyB3aWxsIGJlIGFkZGVk
IGxhdGVyLg0KPiANCj4gU2lnbmVkLW9mZi1ieTogU2FuZGVlcCBTaW5naCA8U2FuZGVlcEBmcmVl
c2NhbGUuY29tPg0KPiBTaWduZWQtb2ZmLWJ5OiBQb29uYW0gQWdncndhbCA8cG9vbmFtLmFnZ3J3
YWxAZnJlZXNjYWxlLmNvbT4NCj4gLS0tDQo+ICBBIGNvdXBsZSBvZiB0b2RvcycgYXJlIGxlZnQg
aW4gdGhlIHBhdGNoLCB3ZSBhcmUgd29ya2luZyBvbiBpdCBhbmQgDQo+IHdpbGwgYmUgYWRkcmVz
c2VkIGluIHRoZSB1cGRhdGVkIHBhdGNoIHNldC4NCj4gIGRyaXZlcnMvS2NvbmZpZyAgICAgICAg
ICAgICAgICAgfCAgICAxICsNCj4gIGRyaXZlcnMvTWFrZWZpbGUgICAgICAgICAgICAgICAgfCAg
ICAxICsNCj4gIGRyaXZlcnMvdGRtL0tjb25maWcgICAgICAgICAgICAgfCAgIDI1ICsNCj4gIGRy
aXZlcnMvdGRtL3RkbS1jb3JlLmMgICAgICAgICAgfCAxMTQ2ICsrKysrKysrKysrKysrKysrKysr
KysrKysrKysrKysrKysrKysrKw0KPiAgaW5jbHVkZS9saW51eC9tb2RfZGV2aWNldGFibGUuaCB8
ICAgMTEgKw0KPiAgaW5jbHVkZS9saW51eC90ZG0uaCAgICAgICAgICAgICB8ICAzNDcgKysrKysr
KysrKysrDQo+ICA2IGZpbGVzIGNoYW5nZWQsIDE1MzEgaW5zZXJ0aW9ucygrKSwgMCBkZWxldGlv
bnMoLSkgIGNyZWF0ZSBtb2RlIA0KPiAxMDA2NDQgZHJpdmVycy90ZG0vS2NvbmZpZyAgY3JlYXRl
IG1vZGUgMTAwNjQ0IGRyaXZlcnMvdGRtL3RkbS1jb3JlLmMgIA0KPiBjcmVhdGUgbW9kZSAxMDA2
NDQgaW5jbHVkZS9saW51eC90ZG0uaA0KPiANCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvS2NvbmZp
ZyBiL2RyaXZlcnMvS2NvbmZpZyBpbmRleCBhZDZjMWViLi4yNWY3ZjViIA0KPiAxMDA2NDQNCj4g
LS0tIGEvZHJpdmVycy9LY29uZmlnDQo+ICsrKyBiL2RyaXZlcnMvS2NvbmZpZw0KPiBAQCAtMTMw
LDQgKzEzMCw1IEBAIHNvdXJjZSAiZHJpdmVycy92aXJ0L0tjb25maWciDQo+ICANCj4gIHNvdXJj
ZSAiZHJpdmVycy9uZXQvZHBhL05ldENvbW1Tdy9LY29uZmlnIg0KPiAgDQo+ICtzb3VyY2UgImRy
aXZlcnMvdGRtL0tjb25maWciDQo+ICBlbmRtZW51DQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL01h
a2VmaWxlIGIvZHJpdmVycy9NYWtlZmlsZSBpbmRleCANCj4gY2Q1NDZlYi4uMzYyYjVlZCAxMDA2
NDQNCj4gLS0tIGEvZHJpdmVycy9NYWtlZmlsZQ0KPiArKysgYi9kcml2ZXJzL01ha2VmaWxlDQo+
IEBAIC0xMDIsNiArMTAyLDcgQEAgb2JqLSQoQ09ORklHX0lORklOSUJBTkQpCSs9IGluZmluaWJh
bmQvDQo+ICBvYmotJChDT05GSUdfU0dJX1NOKQkJKz0gc24vDQo+ICBvYmoteQkJCQkrPSBmaXJt
d2FyZS8NCj4gIG9iai0kKENPTkZJR19DUllQVE8pCQkrPSBjcnlwdG8vDQo+ICtvYmotJChDT05G
SUdfVERNKQkJKz0gdGRtLw0KPiAgb2JqLSQoQ09ORklHX1NVUEVSSCkJCSs9IHNoLw0KPiAgb2Jq
LSQoQ09ORklHX0FSQ0hfU0hNT0JJTEUpCSs9IHNoLw0KPiAgaWZuZGVmIENPTkZJR19BUkNIX1VT
RVNfR0VUVElNRU9GRlNFVCBkaWZmIC0tZ2l0IA0KPiBhL2RyaXZlcnMvdGRtL0tjb25maWcgYi9k
cml2ZXJzL3RkbS9LY29uZmlnIG5ldyBmaWxlIG1vZGUgMTAwNjQ0IGluZGV4IA0KPiAwMDAwMDAw
Li44ZGIyYjA1DQo+IC0tLSAvZGV2L251bGwNCj4gKysrIGIvZHJpdmVycy90ZG0vS2NvbmZpZw0K
PiBAQCAtMCwwICsxLDI1IEBADQo+ICsjDQo+ICsjIFRETSBzdWJzeXN0ZW0gY29uZmlndXJhdGlv
bg0KPiArIw0KPiArDQo+ICttZW51Y29uZmlnIFRETQ0KPiArCXRyaXN0YXRlICJURE0gc3VwcG9y
dCINCj4gKwktLS1oZWxwLS0tDQo+ICsJICBNb3JlIGluZm9ybWF0aW9uIGlzIGNvbnRhaW5lZCBp
biB0aGUgZGlyZWN0b3J5IDxmaWxlOkRvY3VtZW50YXRpb24vdGRtLz4sDQo+ICsJICBlc3BlY2lh
bGx5IGluIHRoZSBmaWxlIGNhbGxlZCAic3VtbWFyeSIgdGhlcmUuDQo+ICsJICBJZiB5b3Ugd2Fu
dCBURE0gc3VwcG9ydCwgeW91IHNob3VsZCBzYXkgWSBoZXJlIGFuZCBhbHNvIHRvIHRoZQ0KPiAr
CSAgc3BlY2lmaWMgZHJpdmVyIGZvciB5b3VyIGJ1cyBhZGFwdGVyKHMpIGJlbG93Lg0KPiArDQo+
ICsJICBUaGlzIFRETSBzdXBwb3J0IGNhbiBhbHNvIGJlIGJ1aWx0IGFzIGEgbW9kdWxlLiAgSWYg
c28sIHRoZSBtb2R1bGUNCj4gKwkgIHdpbGwgYmUgY2FsbGVkIHRkbS1jb3JlLg0KPiArDQo+ICtp
ZiBURE0NCj4gKw0KPiArY29uZmlnIFRETV9ERUJVR19DT1JFDQo+ICsJYm9vbCAiVERNIENvcmUg
ZGVidWdnaW5nIG1lc3NhZ2VzIg0KPiArCWhlbHANCj4gKwkgIFNheSBZIGhlcmUgaWYgeW91IHdh
bnQgdGhlIFRETSBjb3JlIHRvIHByb2R1Y2UgYSBidW5jaCBvZiBkZWJ1Zw0KPiArCSAgbWVzc2Fn
ZXMgdG8gdGhlIHN5c3RlbSBsb2cuICBTZWxlY3QgdGhpcyBpZiB5b3UgYXJlIGhhdmluZyBhDQo+
ICsJICBwcm9ibGVtIHdpdGggVERNIHN1cHBvcnQgYW5kIHdhbnQgdG8gc2VlIG1vcmUgb2Ygd2hh
dCBpcyBnb2luZyBvbi4NCj4gKw0KPiArZW5kaWYgIyBURE0NCj4gZGlmZiAtLWdpdCBhL2RyaXZl
cnMvdGRtL3RkbS1jb3JlLmMgYi9kcml2ZXJzL3RkbS90ZG0tY29yZS5jIG5ldyBmaWxlIA0KPiBt
b2RlIDEwMDY0NCBpbmRleCAwMDAwMDAwLi5jZGRhMjYwDQo+IC0tLSAvZGV2L251bGwNCj4gKysr
IGIvZHJpdmVycy90ZG0vdGRtLWNvcmUuYw0KPiBAQCAtMCwwICsxLDExNDYgQEANCj4gKy8qIGRy
aXZlci90ZG0vdGRtLWNvcmUuYw0KPiArICoNCj4gKyAqIENvcHlyaWdodCAoQykgMjAxMiBGcmVl
c2NhbGUgU2VtaWNvbmR1Y3RvciwgSW5jLCBBbGwgcmlnaHRzIHJlc2VydmVkLg0KPiArICoNCj4g
KyAqIFRETSBjb3JlIGlzIHRoZSBpbnRlcmZhY2UgYmV0d2VlbiBURE0gY2xpZW50cyBhbmQgVERN
IGRldmljZXMuDQo+ICsgKiBJdCBpcyBhbHNvIGludGVuZGVkIHRvIHNlcnZlIGFzIGFuIGludGVy
ZmFjZSBmb3IgbGluZSBjb250cm9sZA0KPiArICogZGV2aWNlcyBsYXRlciBvbi4NCj4gKyAqDQo+
ICsgKiBBdXRob3I6SGVtYW50IEFncmF3YWwgPGhlbWFudEBmcmVlc2NhbGUuY29tPg0KPiArICoJ
UmFqZXNoIEd1bWFzdGEgPHJhamVzaC5ndW1hc3RhQGZyZWVzY2FsZS5jb20+DQo+ICsgKg0KPiAr
ICogTW9kaWZpZWQgYnkgU2FuZGVlcCBLciBTaW5naCA8c2FuZGVlcEBmcmVlc2NhbGUuY29tPg0K
PiArICoJCVBvb25hbSBBZ2dhcndhbCA8cG9vbmFtLmFnZ2Fyd2FsQGZyZWVzY2FsZS5jb20+DQo+
ICsgKiAxLiBBZGRlZCBmcmFtZXdvcmsgYmFzZWQgaW5pdGlhbGl6YXRpb24gb2YgZGV2aWNlLg0K
PiArICogMi4gQWxsIHRoZSBpbml0L3J1biB0aW1lIGNvbmZpZ3VyYXRpb24gaXMgbm93IGRvbmUg
YnkgZnJhbWV3b3JrLg0KPiArICogMy4gQWRkZWQgY2hhbm5lbCBsZXZlbCBvcGVyYXRpb25zLg0K
PiArICoNCj4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlz
dHJpYnV0ZSAgaXQgYW5kL29yIA0KPiArbW9kaWZ5IGl0DQo+ICsgKiB1bmRlciAgdGhlIHRlcm1z
IG9mICB0aGUgR05VIEdlbmVyYWwgIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCANCj4gK2J5
IHRoZQ0KPiArICogRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyAgZWl0aGVyIHZlcnNpb24gMiBv
ZiB0aGUgIExpY2Vuc2UsIG9yIA0KPiArKGF0IHlvdXINCj4gKyAqIG9wdGlvbikgYW55IGxhdGVy
IHZlcnNpb24uDQo+ICsgKg0KPiArICogVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRo
ZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsIA0KPiArYnV0DQo+ICsgKiBXSVRIT1VUIEFO
WSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQo+ICsgKiBN
RVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUg
dGhlIEdOVQ0KPiArICogR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLg0K
PiArICoNCj4gKyAqIFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlICBHTlUg
R2VuZXJhbCBQdWJsaWMgTGljZW5zZSANCj4gK2Fsb25nDQo+ICsgKiB3aXRoIHRoaXMgcHJvZ3Jh
bTsgaWYgbm90LCB3cml0ZSAgdG8gdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgDQo+ICtJ
bmMuLA0KPiArICogNjc1IE1hc3MgQXZlLCBDYW1icmlkZ2UsIE1BIDAyMTM5LCBVU0EuDQo+ICsg
Ki8NCj4gKw0KPiArLyogaWYgcmVhZCB3cml0ZSBkZWJ1ZyByZXF1aXJlZCAqLw0KPiArI3VuZGVm
IFRETV9DT1JFX0RFQlVHDQo+ICsNCj4gKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4NCj4gKyNp
bmNsdWRlIDxsaW51eC9rZXJuZWwuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9lcnJuby5oPg0KPiAr
I2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC90ZG0uaD4NCj4gKyNp
bmNsdWRlIDxsaW51eC9pbml0Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvaWRyLmg+DQo+ICsjaW5j
bHVkZSA8bGludXgvbXV0ZXguaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9jb21wbGV0aW9uLmg+DQo+
ICsjaW5jbHVkZSA8bGludXgvaGFyZGlycS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2lycWZsYWdz
Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvbGlzdC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3VhY2Nl
c3MuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9pby5oPg0KPiArI2luY2x1ZGUgImRldmljZS90ZG1f
ZnNsLmgiDQo+ICsNCj4gKw0KPiArc3RhdGljIERFRklORV9NVVRFWCh0ZG1fY29yZV9sb2NrKTsN
Cj4gK3N0YXRpYyBERUZJTkVfSURSKHRkbV9hZGFwdGVyX2lkcik7DQo+ICsvKiBMaXN0IG9mIFRE
TSBhZGFwdGVycyByZWdpc3RlcmVkIHdpdGggVERNIGZyYW1ld29yayAqLyANCj4gK0xJU1RfSEVB
RChhZGFwdGVyX2xpc3QpOw0KPiArDQo+ICsvKiBMaXN0IG9mIFRETSBjbGllbnRzIHJlZ2lzdGVy
ZWQgd2l0aCBURE0gZnJhbWV3b3JrICovIA0KPiArTElTVF9IRUFEKGRyaXZlcl9saXN0KTsNCj4g
Kw0KPiArLyogSW4gY2FzZSB0aGUgcHJldmlvdXMgZGF0YSBpcyBub3QgZmV0Y2hlZCBieSB0aGUg
Y2xpZW50IGRyaXZlciwgdGhlDQo+ICsgKiBkZS1pbnRlcmxlYXZpbmcgZnVuY3Rpb24gd2lsbCAg
ZGlzY2FyZCB0aGUgb2xkIGRhdGEgYW5kIHJld3JpdGUgDQo+ICt0aGUNCj4gKyAqIG5ldyBkYXRh
ICovDQo+ICtzdGF0aWMgaW50IHVzZV9sYXRlc3RfdGRtX2RhdGEgPSAxOw0KPiArDQo+ICsvKiB0
aGlzIHRhc2tsZXQgaXMgY3JlYXRlZCBmb3IgZWFjaCBhZGFwdGVyIGluc3RhbmNlICovIHN0YXRp
YyB2b2lkIA0KPiArdGRtX2RhdGFfdGFza2xldF9mbih1bnNpZ25lZCBsb25nKTsNCj4gKw0KPiAr
LyogdHJpZXMgdG8gbWF0Y2ggY2xpZW50IGRyaXZlciB3aXRoIHRoZSBhZGFwdGVyICovIHN0YXRp
YyBpbnQgDQo+ICt0ZG1fZGV2aWNlX21hdGNoKHN0cnVjdCB0ZG1fZHJpdmVyICpkcml2ZXIsIHN0
cnVjdCB0ZG1fYWRhcHRlciAqYWRhcCkgDQo+ICt7DQo+ICsJLyogbWF0Y2ggb24gYW4gaWQgdGFi
bGUgaWYgdGhlcmUgaXMgb25lICovDQo+ICsJaWYgKGRyaXZlci0+aWRfdGFibGUgJiYgZHJpdmVy
LT5pZF90YWJsZS0+bmFtZVswXSkgew0KPiArCQlpZiAoIShzdHJjbXAoZHJpdmVyLT5pZF90YWJs
ZS0+bmFtZSwgYWRhcC0+bmFtZSkpKQ0KPiArCQkJcmV0dXJuIChpbnQpZHJpdmVyLT5pZF90YWJs
ZTsNCj4gKwl9DQo+ICsJcmV0dXJuIFRETV9FX09LOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW50
IHRkbV9hdHRhY2hfZHJpdmVyX2FkYXAoc3RydWN0IHRkbV9kcml2ZXIgKmRyaXZlciwNCj4gKwkJ
CQkJc3RydWN0IHRkbV9hZGFwdGVyICphZGFwKQ0KPiArew0KPiArCS8qIGlmIGRyaXZlciBpcyBh
bHJlYWR5IGF0dGFjaGVkIHRvIGFueSBvdGhlciBhZGFwdGVyLCByZXR1cm4qLw0KPiArCWlmIChk
cml2ZXItPmFkYXB0ZXIgJiYgKGRyaXZlci0+YWRhcHRlciAhPSBhZGFwKSkNCj4gKwkJcmV0dXJu
IFRETV9FX09LOw0KPiArDQo+ICsJZHJpdmVyLT5hZGFwdGVyID0gYWRhcDsNCj4gKw0KPiArCWlm
IChkcml2ZXItPmF0dGFjaF9hZGFwdGVyKSB7DQo+ICsJCWlmIChkcml2ZXItPmF0dGFjaF9hZGFw
dGVyKGFkYXApIDwgMCkNCj4gKwkJCS8qIFdlIGlnbm9yZSB0aGUgcmV0dXJuIGNvZGU7IGlmIGl0
IGZhaWxzLCB0b28gYmFkICovDQo+ICsJCQlwcl9lcnIoImF0dGFjaF9hZGFwdGVyIGZhaWxlZCBm
b3IgZHJpdmVyIFslc11cbiIsDQo+ICsJCQkJZHJpdmVyLT5uYW1lKTsNCj4gKwl9DQo+ICsJYWRh
cC0+ZHJ2X2NvdW50Kys7DQo+ICsNCj4gKwlpZiAoIWFkYXAtPnRhc2tsZXRfY29uZikgew0KPiAr
CQl0YXNrbGV0X2luaXQoJmFkYXAtPnRkbV9kYXRhX3Rhc2tsZXQsIHRkbV9kYXRhX3Rhc2tsZXRf
Zm4sDQo+ICsJCQkJCQkodW5zaWduZWQgbG9uZylhZGFwKTsNCj4gKwkJYWRhcC0+dGFza2xldF9j
b25mID0gMTsNCj4gKwl9DQo+ICsNCj4gKwlyZXR1cm4gVERNX0VfT0s7DQo+ICt9DQo+ICsNCj4g
Ky8qIERldGFjaCBjbGllbnQgZHJpdmVyIGFuZCBhZGFwdGVyICovIHN0YXRpYyBpbnQgDQo+ICt0
ZG1fZGV0YWNoX2RyaXZlcl9hZGFwKHN0cnVjdCB0ZG1fZHJpdmVyICpkcml2ZXIsDQo+ICsJCQkJ
CXN0cnVjdCB0ZG1fYWRhcHRlciAqYWRhcCkNCj4gK3sNCj4gKwlpbnQgcmVzID0gVERNX0VfT0s7
DQo+ICsNCj4gKwlpZiAoIWRyaXZlci0+YWRhcHRlciB8fCAoZHJpdmVyLT5hZGFwdGVyICE9IGFk
YXApKQ0KPiArCQlyZXR1cm4gVERNX0VfT0s7DQo+ICsNCj4gKwlpZiAoIWRyaXZlci0+ZGV0YWNo
X2FkYXB0ZXIpDQo+ICsJCXJldHVybiBURE1fRV9PSzsNCj4gKw0KPiArCWFkYXAtPmRydl9jb3Vu
dC0tOw0KPiArDQo+ICsJLyogSWYgbm8gbW9yZSBkcml2ZXIgaXMgcmVnaXN0ZWQgd2l0aCB0aGUg
YWRhcHRlciovDQo+ICsJaWYgKCFhZGFwLT5kcnZfY291bnQgJiYgYWRhcC0+dGFza2xldF9jb25m
KSB7DQo+ICsJCXRhc2tsZXRfZGlzYWJsZSgmYWRhcC0+dGRtX2RhdGFfdGFza2xldCk7DQo+ICsJ
CXRhc2tsZXRfa2lsbCgmYWRhcC0+dGRtX2RhdGFfdGFza2xldCk7DQo+ICsJCWFkYXAtPnRhc2ts
ZXRfY29uZiA9IDA7DQo+ICsJfQ0KPiArDQo+ICsJaWYgKGRyaXZlci0+ZGV0YWNoX2FkYXB0ZXIp
IHsNCj4gKwkJaWYgKGRyaXZlci0+ZGV0YWNoX2FkYXB0ZXIoYWRhcCkpDQo+ICsJCQlwcl9lcnIo
ImRldGFjaF9hZGFwdGVyIGZhaWxlZCBmb3IgZHJpdmVyIFslc11cbiIsDQo+ICsJCQkJZHJpdmVy
LT5uYW1lKTsNCj4gKwl9DQo+ICsNCj4gKwlkcml2ZXItPmFkYXB0ZXIgPSBOVUxMOw0KPiArCXJl
dHVybiByZXM7DQo+ICt9DQo+ICsNCj4gKy8qIFRETSBhZGFwdGVyIFJlZ2lzdHJhdGlvbi9EZS1y
ZWdpc3RyYXRpb24gd2l0aCBURE0gZnJhbWV3b3JrICovDQo+ICsNCj4gK3N0YXRpYyBpbnQgdGRt
X3JlZ2lzdGVyX2FkYXB0ZXIoc3RydWN0IHRkbV9hZGFwdGVyICphZGFwKSB7DQo+ICsJaW50IHJl
cyA9IFRETV9FX09LOw0KPiArCXN0cnVjdCB0ZG1fZHJpdmVyICpkcml2ZXIsICpuZXh0Ow0KPiAr
DQo+ICsJaWYgKCFhZGFwKSB7DQo+ICsJCXByX2VycigiJXM6SW52YWxpZCBoYW5kbGVcbiIsIF9f
ZnVuY19fKTsNCj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ICsJfQ0KPiArDQo+ICsJbXV0ZXhfaW5p
dCgmYWRhcC0+YWRhcF9sb2NrKTsNCj4gKwlJTklUX0xJU1RfSEVBRCgmYWRhcC0+bXlwb3J0cyk7
DQo+ICsJc3Bpbl9sb2NrX2luaXQoJmFkYXAtPnBvcnRsaXN0X2xvY2spOw0KPiArDQo+ICsJYWRh
cC0+ZHJ2X2NvdW50ID0gMDsNCj4gKwlhZGFwLT50YXNrbGV0X2NvbmYgPSAwOw0KPiArDQo+ICsJ
bGlzdF9hZGRfdGFpbCgmYWRhcC0+bGlzdCwgJmFkYXB0ZXJfbGlzdCk7DQo+ICsNCj4gKwkvKiBp
bml0aWFsaXphdGlvbiBvZiBkcml2ZXIgYnkgZnJhbWV3b3JrIGluIGRlZmF1bHQgY29uZmlndXJh
dGlvbiAqLw0KPiArCWluaXRfY29uZmlnX2FkYXB0ZXIoYWRhcCk7DQo+ICsNCj4gKwkvKiBOb3Rp
ZnkgZHJpdmVycyAqLw0KPiArCXByX2luZm8oImFkYXB0ZXIgWyVzXSByZWdpc3RlcmVkXG4iLCBh
ZGFwLT5uYW1lKTsNCj4gKwltdXRleF9sb2NrKCZ0ZG1fY29yZV9sb2NrKTsNCj4gKwlsaXN0X2Zv
cl9lYWNoX2VudHJ5X3NhZmUoZHJpdmVyLCBuZXh0LCAmZHJpdmVyX2xpc3QsIGxpc3QpIHsNCj4g
KwkJaWYgKHRkbV9kZXZpY2VfbWF0Y2goZHJpdmVyLCBhZGFwKSkgew0KPiArCQkJcmVzID0gdGRt
X2F0dGFjaF9kcml2ZXJfYWRhcChkcml2ZXIsIGFkYXApOw0KPiArCQkJcHJfaW5mbygNCj4gKwkJ
CSJEcml2ZXIoSUQ9JWQpIGlzIGF0dGFjaGVkIHdpdGggQWRhcHRlciAlcyhJRCA9ICVkKVxuIiwN
Cj4gKwkJCQlkcml2ZXItPmlkLCBhZGFwLT5uYW1lLCBhZGFwLT5pZCk7DQo+ICsJCX0NCj4gKwl9
DQo+ICsJbXV0ZXhfdW5sb2NrKCZ0ZG1fY29yZV9sb2NrKTsNCj4gKw0KPiArCXJldHVybiByZXM7
DQo+ICt9DQo+ICsNCj4gKy8qDQo+ICsgKiB0ZG1fYWRkX2FkYXB0ZXIgLSBkZWNsYXJlIHRkbSBh
ZGFwdGVyLCB1c2UgZHluYW1pYyBkZXZpY2UgbnVtYmVyDQo+ICsgKiBAYWRhcHRlcjogdGhlIGFk
YXB0ZXIgdG8gYWRkDQo+ICsgKiBDb250ZXh0OiBjYW4gc2xlZXANCj4gKyAqDQo+ICsgKiBUaGlz
IHJvdXRpbmUgaXMgdXNlZCB0byBkZWNsYXJlIGEgVERNIGFkYXB0ZXINCj4gKyAqIFdoZW4gdGhp
cyByZXR1cm5zIHplcm8sIGEgbmV3IGRldmljZSBudW1iZXIgd2lsbCBiZSBhbGxvY2F0ZWQgYW5k
IA0KPiArc3RvcmVkDQo+ICsgKiBpbiBhZGFwLT5pZCwgYW5kIHRoZSBzcGVjaWZpZWQgYWRhcHRl
ciBiZWNhbWUgYXZhaWxhYmxlIGZvciB0aGUgY2xpZW50cy4NCj4gKyAqIE90aGVyd2lzZSwgYSBu
ZWdhdGl2ZSBlcnJubyB2YWx1ZSBpcyByZXR1cm5lZC4NCj4gKyAqLw0KPiAraW50IHRkbV9hZGRf
YWRhcHRlcihzdHJ1Y3QgdGRtX2FkYXB0ZXIgKmFkYXB0ZXIpIHsNCj4gKwlpbnQgaWQsIHJlcyA9
IFRETV9FX09LOw0KPiArCWlmICghYWRhcHRlcikgew0KPiArCQlwcl9lcnIoIiVzOkludmFsaWQg
aGFuZGxlXG4iLCBfX2Z1bmNfXyk7DQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiArCX0NCj4gKw0K
PiArcmV0cnk6DQo+ICsJaWYgKGlkcl9wcmVfZ2V0KCZ0ZG1fYWRhcHRlcl9pZHIsIEdGUF9LRVJO
RUwpID09IDApDQo+ICsJCXJldHVybiAtRU5PTUVNOw0KPiArDQo+ICsJbXV0ZXhfbG9jaygmdGRt
X2NvcmVfbG9jayk7DQo+ICsJcmVzID0gaWRyX2dldF9uZXcoJnRkbV9hZGFwdGVyX2lkciwgYWRh
cHRlciwgJmlkKTsNCj4gKwltdXRleF91bmxvY2soJnRkbV9jb3JlX2xvY2spOw0KPiArDQo+ICsJ
aWYgKHJlcyA8IDApIHsNCj4gKwkJaWYgKHJlcyA9PSAtRUFHQUlOKQ0KPiArCQkJZ290byByZXRy
eTsNCj4gKwkJcmV0dXJuIHJlczsNCj4gKwl9DQo+ICsNCj4gKwlhZGFwdGVyLT5pZCA9IGlkOw0K
PiArCXJldHVybiB0ZG1fcmVnaXN0ZXJfYWRhcHRlcihhZGFwdGVyKTsgfSANCj4gK0VYUE9SVF9T
WU1CT0wodGRtX2FkZF9hZGFwdGVyKTsNCj4gKw0KPiArDQo+ICsvKioNCj4gKyAqIHRkbV9kZWxf
YWRhcHRlciAtIHVucmVnaXN0ZXIgVERNIGFkYXB0ZXINCj4gKyAqIEBhZGFwOiB0aGUgYWRhcHRl
ciBiZWluZyB1bnJlZ2lzdGVyZWQNCj4gKyAqDQo+ICsgKiBUaGlzIHVucmVnaXN0ZXJzIGFuIFRE
TSBhZGFwdGVyIHdoaWNoIHdhcyBwcmV2aW91c2x5IHJlZ2lzdGVyZWQNCj4gKyAqIGJ5IEB0ZG1f
YWRkX2FkYXB0ZXIuDQo+ICsgKi8NCj4gK2ludCB0ZG1fZGVsX2FkYXB0ZXIoc3RydWN0IHRkbV9h
ZGFwdGVyICphZGFwKSB7DQo+ICsJaW50IHJlcyA9IFRETV9FX09LOw0KPiArCXN0cnVjdCB0ZG1f
YWRhcHRlciAqZm91bmQ7DQo+ICsJc3RydWN0IHRkbV9kcml2ZXIgKmRyaXZlciwgKm5leHQ7DQo+
ICsNCj4gKwlpZiAoIWFkYXApIHsNCj4gKwkJcHJfZXJyKCIlczpJbnZhbGlkIGhhbmRsZVxuIiwg
X19mdW5jX18pOw0KPiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gKwl9DQo+ICsNCj4gKwkvKiBGaXJz
dCBtYWtlIHN1cmUgdGhhdCB0aGlzIGFkYXB0ZXIgd2FzIGV2ZXIgYWRkZWQgKi8NCj4gKwltdXRl
eF9sb2NrKCZ0ZG1fY29yZV9sb2NrKTsNCj4gKwlmb3VuZCA9IGlkcl9maW5kKCZ0ZG1fYWRhcHRl
cl9pZHIsIGFkYXAtPmlkKTsNCj4gKwltdXRleF91bmxvY2soJnRkbV9jb3JlX2xvY2spOw0KPiAr
CWlmIChmb3VuZCAhPSBhZGFwKSB7DQo+ICsJCXByX2VycigidGRtLWNvcmU6IGF0dGVtcHRpbmcg
dG8gZGVsZXRlIHVucmVnaXN0ZXJlZCAiDQo+ICsJCQkgImFkYXB0ZXIgWyVzXVxuIiwgYWRhcC0+
bmFtZSk7DQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiArCX0NCj4gKw0KPiArCS8qZGlzYWJsZSBh
bmQga2lsbCB0aGUgZGF0YSBwcm9jZXNzaW5nIHRhc2tsZXQgKi8NCj4gKwlpZiAoYWRhcC0+dGFz
a2xldF9jb25mKSB7DQo+ICsJCXRhc2tsZXRfZGlzYWJsZSgmYWRhcC0+dGRtX2RhdGFfdGFza2xl
dCk7DQo+ICsJCXRhc2tsZXRfa2lsbCgmYWRhcC0+dGRtX2RhdGFfdGFza2xldCk7DQo+ICsJCWFk
YXAtPnRhc2tsZXRfY29uZiA9IDA7DQo+ICsJfQ0KPiArDQo+ICsJLyogRGV0YWNoIGFueSBhY3Rp
dmUgcG9ydHMuIFRoaXMgY2FuJ3QgZmFpbCwgdGh1cyB3ZSBkbyBub3QNCj4gKwkgICBjaGVja2lu
ZyB0aGUgcmV0dXJuZWQgdmFsdWUuICovDQo+ICsJbXV0ZXhfbG9jaygmdGRtX2NvcmVfbG9jayk7
DQo+ICsJbGlzdF9mb3JfZWFjaF9lbnRyeV9zYWZlKGRyaXZlciwgbmV4dCwgJmRyaXZlcl9saXN0
LCBsaXN0KSB7DQo+ICsJCWlmICh0ZG1fZGV2aWNlX21hdGNoKGRyaXZlciwgYWRhcCkpIHsNCj4g
KwkJCXRkbV9kZXRhY2hfZHJpdmVyX2FkYXAoZHJpdmVyLCBhZGFwKTsNCj4gKwkJCXByX2luZm8o
DQo+ICsJCQkiRHJpdmVyKElEPSVkKSBpcyBkZXRhY2hlZCBmcm9tIEFkYXB0ZXIgJXMoSUQgPSAl
ZClcbiIsDQo+ICsJCQkJIGRyaXZlci0+aWQsIGFkYXAtPm5hbWUsIGFkYXAtPmlkKTsNCj4gKwkJ
fQ0KPiArCX0NCj4gKwltdXRleF91bmxvY2soJnRkbV9jb3JlX2xvY2spOw0KPiArDQo+ICsJbXV0
ZXhfbG9jaygmdGRtX2NvcmVfbG9jayk7DQo+ICsJaWRyX3JlbW92ZSgmdGRtX2FkYXB0ZXJfaWRy
LCBhZGFwLT5pZCk7DQo+ICsJbXV0ZXhfdW5sb2NrKCZ0ZG1fY29yZV9sb2NrKTsNCj4gKw0KPiAr
CXByX2RlYnVnKCJhZGFwdGVyIFslc10gdW5yZWdpc3RlcmVkXG4iLCBhZGFwLT5uYW1lKTsNCj4g
Kw0KPiArCWxpc3RfZGVsKCZhZGFwLT5saXN0KTsNCj4gKwkvKiBDbGVhciB0aGUgZGV2aWNlIHN0
cnVjdHVyZSBpbiBjYXNlIHRoaXMgYWRhcHRlciBpcyBldmVyIGdvaW5nIHRvIGJlDQo+ICsJICAg
YWRkZWQgYWdhaW4gKi8NCj4gKwlhZGFwLT5wYXJlbnQgPSBOVUxMOw0KPiArDQo+ICsJcmV0dXJu
IHJlczsNCj4gK30NCj4gK0VYUE9SVF9TWU1CT0wodGRtX2RlbF9hZGFwdGVyKTsNCj4gKw0KPiAr
LyogVERNIENsaWVudCBEcml2ZXJzIFJlZ2lzdHJhdGlvbi9EZS1yZWdpc3RyYXRpb24gRnVuY3Rp
b25zICovIGludCANCj4gK3RkbV9yZWdpc3Rlcl9kcml2ZXIoc3RydWN0IHRkbV9kcml2ZXIgKmRy
aXZlcikgew0KPiArCWludCByZXMgPSBURE1fRV9PSzsNCj4gKwlzdHJ1Y3QgdGRtX2FkYXB0ZXIg
KmFkYXAsICpuZXh0Ow0KPiArDQo+ICsJbGlzdF9hZGRfdGFpbCgmZHJpdmVyLT5saXN0LCAmZHJp
dmVyX2xpc3QpOw0KPiArDQo+ICsJbXV0ZXhfbG9jaygmdGRtX2NvcmVfbG9jayk7DQo+ICsJLyog
V2FsayB0aGUgYWRhcHRlcnMgdGhhdCBhcmUgYWxyZWFkeSBwcmVzZW50ICovDQo+ICsJbGlzdF9m
b3JfZWFjaF9lbnRyeV9zYWZlKGFkYXAsIG5leHQsICZhZGFwdGVyX2xpc3QsIGxpc3QpIHsNCj4g
KwkJaWYgKHRkbV9kZXZpY2VfbWF0Y2goZHJpdmVyLCBhZGFwKSkgew0KPiArCQkJcmVzID0gdGRt
X2F0dGFjaF9kcml2ZXJfYWRhcChkcml2ZXIsIGFkYXApOw0KPiArCQkJcHJfaW5mbygiVERNIERy
aXZlcihJRD0lZClpcyBhdHRhY2hlZCB3aXRoIEFkYXB0ZXIiDQo+ICsJCQkJIiVzKElEID0gJWQp
IGRydl9jb3VudD0lZCIsIGRyaXZlci0+aWQsDQo+ICsJCQkJYWRhcC0+bmFtZSwgYWRhcC0+aWQs
IGFkYXAtPmRydl9jb3VudCk7DQo+ICsJCWJyZWFrOw0KPiArCQl9DQo+ICsJfQ0KPiArCW11dGV4
X3VubG9jaygmdGRtX2NvcmVfbG9jayk7DQo+ICsNCj4gKwlyZXR1cm4gcmVzOw0KPiArfQ0KPiAr
RVhQT1JUX1NZTUJPTCh0ZG1fcmVnaXN0ZXJfZHJpdmVyKTsNCj4gKw0KPiArLyoNCj4gKyAqIHRk
bV91bnJlZ2lzdGVyX2RyaXZlciAtIHVucmVnaXN0ZXIgVERNIGNsaWVudCBkcml2ZXIgZnJvbSBU
RE0gDQo+ICtmcmFtZXdvcmsNCj4gKyAqIEBkcml2ZXI6IHRoZSBkcml2ZXIgYmVpbmcgdW5yZWdp
c3RlcmVkICAqLyB2b2lkIA0KPiArdGRtX3VucmVnaXN0ZXJfZHJpdmVyKHN0cnVjdCB0ZG1fZHJp
dmVyICpkcml2ZXIpIHsNCj4gKwlpZiAoIWRyaXZlcikgew0KPiArCQlwcl9lcnIoIiVzOkludmFs
aWQgaGFuZGxlXG4iLCBfX2Z1bmNfXyk7DQo+ICsJCXJldHVybjsNCj4gKwl9DQo+ICsgICAgICAg
LyogQSBkcml2ZXIgY2FuIHJlZ2lzdGVyIHRvIG9ubHkgb25lIGFkYXB0ZXIsDQo+ICsJKiBzbyBu
byBuZWVkIHRvIGJyb3dzZSB0aGUgbGlzdCAqLw0KPiArCW11dGV4X2xvY2soJnRkbV9jb3JlX2xv
Y2spOw0KPiArCXRkbV9kZXRhY2hfZHJpdmVyX2FkYXAoZHJpdmVyLCBkcml2ZXItPmFkYXB0ZXIp
Ow0KPiArCW11dGV4X3VubG9jaygmdGRtX2NvcmVfbG9jayk7DQo+ICsNCj4gKwlsaXN0X2RlbCgm
ZHJpdmVyLT5saXN0KTsNCj4gKw0KPiArCXByX2RlYnVnKCJ0ZG0tY29yZTogZHJpdmVyIFslc10g
dW5yZWdpc3RlcmVkXG4iLCBkcml2ZXItPm5hbWUpOyB9IA0KPiArRVhQT1JUX1NZTUJPTCh0ZG1f
dW5yZWdpc3Rlcl9kcml2ZXIpOw0KPiArDQo+ICsvKiBURE0gRnJhbWV3b3JrIGluaXQgYW5kIGV4
aXQgKi8NCj4gK3N0YXRpYyBpbnQgX19pbml0IHRkbV9pbml0KHZvaWQpDQo+ICt7DQo+ICsJcHJf
aW5mbygiJXNcbiIsIF9fZnVuY19fKTsNCj4gKwlyZXR1cm4gVERNX0VfT0s7DQo+ICt9DQo+ICsN
Cj4gK3N0YXRpYyB2b2lkIF9fZXhpdCB0ZG1fZXhpdCh2b2lkKQ0KPiArew0KPiArCXByX2luZm8o
IiVzXG4iLCBfX2Z1bmNfXyk7DQo+ICsJcmV0dXJuOw0KPiArfQ0KPiArDQo+ICsvKiBXZSBtdXN0
IGluaXRpYWxpemUgZWFybHksIGJlY2F1c2Ugc29tZSBzdWJzeXN0ZW1zIHJlZ2lzdGVyIHRkbSAN
Cj4gK2RyaXZlcnMNCj4gKyAqIGluIHN1YnN5c19pbml0Y2FsbCgpIGNvZGUsIGJ1dCBhcmUgbGlu
a2VkIChhbmQgaW5pdGlhbGl6ZWQpIGJlZm9yZSB0ZG0uDQo+ICsgKi8NCj4gK3Bvc3Rjb3JlX2lu
aXRjYWxsKHRkbV9pbml0KTsNCj4gK21vZHVsZV9leGl0KHRkbV9leGl0KTsNCj4gKw0KPiArDQo+
ICsvKiBJbnRlcmZhY2UgdG8gdGhlIHRkbSBkZXZpY2UvYWRhcHRlciAqLw0KPiArDQo+ICsvKiB0
ZG1fYWRhcF9zZW5kIC0gaXNzdWUgYSBURE0gd3JpdGUNCj4gKyAqIEBhZGFwOiBIYW5kbGUgdG8g
VERNIGRldmljZQ0KPiArICogQGJ1ZjogRGF0YSB0aGF0IHdpbGwgYmUgd3JpdHRlbiB0byB0aGUg
VERNIGRldmljZQ0KPiArICogQGNvdW50OiBIb3cgbWFueSBieXRlcyB0byB3cml0ZQ0KPiArICoN
Cj4gKyAqIFJldHVybnMgbmVnYXRpdmUgZXJybm8sIG9yIGVsc2UgdGhlIG51bWJlciBvZiBieXRl
cyB3cml0dGVuLg0KPiArICovDQo+ICtpbnQgdGRtX2FkYXBfc2VuZChzdHJ1Y3QgdGRtX2FkYXB0
ZXIgKmFkYXAsIHZvaWQgKipidWYsIGludCBjb3VudCkgew0KPiArCWludCByZXM7DQo+ICsNCj4g
KwlpZiAoKGFkYXAgPT0gTlVMTCkgfHwgKGJ1ZiA9PSBOVUxMKSkgeyAvKiBpbnZhbGlkIGhhbmRs
ZSovDQo+ICsJCXByX2VycigiJXM6IEludmFsaWQgSGFuZGxlXG4iLCBfX2Z1bmNfXyk7DQo+ICsJ
CXJldHVybiAtRU5YSU87DQo+ICsJfQ0KPiArDQo+ICsJaWYgKGFkYXAtPmFsZ28tPnRkbV93cml0
ZSkNCj4gKwkJcmVzID0gYWRhcC0+YWxnby0+dGRtX3dyaXRlKGFkYXAsIGJ1ZiwgY291bnQpOw0K
PiArCWVsc2Ugew0KPiArCQlwcl9lcnIoIlRETSBsZXZlbCB3cml0ZSBub3Qgc3VwcG9ydGVkXG4i
KTsNCj4gKwkJcmV0dXJuIC1FT1BOT1RTVVBQOw0KPiArCX0NCj4gKw0KPiArCS8qIElmIGV2ZXJ5
dGhpbmcgd2VudCBvayAoaS5lLiBmcmFtZSB0cmFuc21pdHRlZCksIHJldHVybiAjYnl0ZXMNCj4g
KwkgICB0cmFuc21pdHRlZCwgZWxzZSBlcnJvciBjb2RlLiAqLw0KPiArCXJldHVybiAocmVzID09
IDEpID8gY291bnQgOiByZXM7DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9hZGFwX3NlbmQp
Ow0KPiArDQo+ICsvKioNCj4gKyAqIHRkbV9hZGFwX3JlY3YgLSBpc3N1ZSBhIFRETSByZWFkDQo+
ICsgKiBAYWRhcDogSGFuZGxlIHRvIFRETSBkZXZpY2UNCj4gKyAqIEBidWY6IFdoZXJlIHRvIHN0
b3JlIGRhdGEgcmVhZCBmcm9tIFRETSBkZXZpY2UNCj4gKyAqDQo+ICsgKiBSZXR1cm5zIG5lZ2F0
aXZlIGVycm5vLCBvciBlbHNlIHRoZSBudW1iZXIgb2YgYnl0ZXMgcmVhZC4NCj4gKyAqLw0KPiAr
aW50IHRkbV9hZGFwX3JlY3Yoc3RydWN0IHRkbV9hZGFwdGVyICphZGFwLCB2b2lkICoqYnVmKSB7
DQo+ICsJaW50IHJlczsNCj4gKw0KPiArCWlmIChhZGFwLT5hbGdvLT50ZG1fcmVhZCkNCj4gKwkJ
cmVzID0gYWRhcC0+YWxnby0+dGRtX3JlYWQoYWRhcCwgKHUxNiAqKilidWYpOw0KPiArCWVsc2Ug
ew0KPiArCQlwcl9lcnIoIlRETSBsZXZlbCByZWFkIG5vdCBzdXBwb3J0ZWRcbiIpOw0KPiArCQly
ZXR1cm4gLUVPUE5PVFNVUFA7DQo+ICsJfQ0KPiArCS8qIElmIGV2ZXJ5dGhpbmcgd2VudCBvayAo
aS5lLiBmcmFtZSByZWNlaXZlZCksIHJldHVybiAjYnl0ZXMNCj4gKwkgICB0cmFuc21pdHRlZCwg
ZWxzZSBlcnJvciBjb2RlLiAqLw0KPiArCXJldHVybiByZXM7DQo+ICt9DQo+ICsNCj4gKy8qKg0K
PiArICogdGRtX2FkYXBfZ2V0X3dyaXRlX2J1ZiAtIGdldCBuZXh0IHdyaXRlIFRETSBkZXZpY2Ug
YnVmZmVyDQo+ICsgKiBAYWRhcDogSGFuZGxlIHRvIFRETSBkZXZpY2UNCj4gKyAqIEBidWY6IHBv
aW50ZXIgdG8gVERNIGRldmljZSBidWZmZXINCj4gKyAqDQo+ICsgKiBSZXR1cm5zIG5lZ2F0aXZl
IGVycm5vLCBvciBlbHNlIHNpemUgb2YgdGhlIHdyaXRlIGJ1ZmZlci4NCj4gKyAqLw0KPiAraW50
IHRkbV9hZGFwX2dldF93cml0ZV9idWYoc3RydWN0IHRkbV9hZGFwdGVyICphZGFwLCB2b2lkICoq
YnVmKSB7DQo+ICsJaW50IHJlczsNCj4gKw0KPiArCWlmIChhZGFwLT5hbGdvLT50ZG1fZ2V0X3dy
aXRlX2J1Zikgew0KPiArCQlyZXMgPSBhZGFwLT5hbGdvLT50ZG1fZ2V0X3dyaXRlX2J1ZihhZGFw
LCAodTE2ICoqKWJ1Zik7DQo+ICsJfSBlbHNlIHsNCj4gKwkJcHJfZXJyKCJURE0gbGV2ZWwgd3Jp
dGUgYnVmIGdldCBub3Qgc3VwcG9ydGVkXG4iKTsNCj4gKwkJcmV0dXJuIC1FT1BOT1RTVVBQOw0K
PiArCX0NCj4gKwkvKiBJZiBldmVyeXRoaW5nIHdlbnQgb2sgKGkuZS4gMSBtc2cgcmVjZWl2ZWQp
LCByZXR1cm4gI2J5dGVzDQo+ICsJICAgdHJhbnNtaXR0ZWQsIGVsc2UgZXJyb3IgY29kZS4gKi8N
Cj4gKwlyZXR1cm4gcmVzOw0KPiArfQ0KPiArRVhQT1JUX1NZTUJPTCh0ZG1fYWRhcF9nZXRfd3Jp
dGVfYnVmKTsNCj4gKw0KPiAraW50IHRkbV9hZGFwX2VuYWJsZShzdHJ1Y3QgdGRtX2RyaXZlciAq
ZHJ2KSB7DQo+ICsJaW50IHJlczsNCj4gKwlzdHJ1Y3QgdGRtX2FkYXB0ZXIgKmFkYXA7DQo+ICsJ
aWYgKGRydiA9PSBOVUxMKSB7IC8qIGludmFsaWQgaGFuZGxlKi8NCj4gKwkJcHJfZXJyKCIlczog
SW52YWxpZCBIYW5kbGVcbiIsIF9fZnVuY19fKTsNCj4gKwkJcmV0dXJuIC1FTlhJTzsNCj4gKwl9
DQo+ICsJYWRhcCA9IGRydi0+YWRhcHRlcjsNCj4gKw0KPiArCWlmIChhZGFwLT5hbGdvLT50ZG1f
ZW5hYmxlKSB7DQo+ICsJCXJlcyA9IGFkYXAtPmFsZ28tPnRkbV9lbmFibGUoYWRhcCk7DQo+ICsJ
fSBlbHNlIHsNCj4gKwkJcHJfZXJyKCJURE0gbGV2ZWwgZW5hYmxlIG5vdCBzdXBwb3J0ZWRcbiIp
Ow0KPiArCQlyZXR1cm4gLUVPUE5PVFNVUFA7DQo+ICsJfQ0KPiArCXJldHVybiByZXM7DQo+ICt9
DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9hZGFwX2VuYWJsZSk7DQo+ICsNCj4gK2ludCB0ZG1fYWRh
cF9kaXNhYmxlKHN0cnVjdCB0ZG1fZHJpdmVyICpkcnYpIHsNCj4gKwlpbnQgcmVzOw0KPiArCXN0
cnVjdCB0ZG1fYWRhcHRlciAqYWRhcDsNCj4gKwlpZiAoZHJ2ID09IE5VTEwpIHsgLyogaW52YWxp
ZCBoYW5kbGUqLw0KPiArCQlwcl9lcnIoIiVzOiBJbnZhbGlkIEhhbmRsZVxuIiwgX19mdW5jX18p
Ow0KPiArCQlyZXR1cm4gLUVOWElPOw0KPiArCX0NCj4gKwlhZGFwID0gZHJ2LT5hZGFwdGVyOw0K
PiArDQo+ICsJaWYgKGFkYXAtPmFsZ28tPnRkbV9kaXNhYmxlKSB7DQo+ICsJCXJlcyA9IGFkYXAt
PmFsZ28tPnRkbV9kaXNhYmxlKGFkYXApOw0KPiArCX0gZWxzZSB7DQo+ICsJCXByX2VycigiVERN
IGxldmVsIGVuYWJsZSBub3Qgc3VwcG9ydGVkXG4iKTsNCj4gKwkJcmV0dXJuIC1FT1BOT1RTVVBQ
Ow0KPiArCX0NCj4gKwlyZXR1cm4gcmVzOw0KPiArfQ0KPiArRVhQT1JUX1NZTUJPTCh0ZG1fYWRh
cF9kaXNhYmxlKTsNCj4gKw0KPiArc3RydWN0IHRkbV9hZGFwdGVyICp0ZG1fZ2V0X2FkYXB0ZXIo
aW50IGlkKSB7DQo+ICsJc3RydWN0IHRkbV9hZGFwdGVyICphZGFwdGVyOw0KPiArDQo+ICsJbXV0
ZXhfbG9jaygmdGRtX2NvcmVfbG9jayk7DQo+ICsJYWRhcHRlciA9IGlkcl9maW5kKCZ0ZG1fYWRh
cHRlcl9pZHIsIGlkKTsNCj4gKwlpZiAoYWRhcHRlciAmJiAhdHJ5X21vZHVsZV9nZXQoYWRhcHRl
ci0+b3duZXIpKQ0KPiArCQlhZGFwdGVyID0gTlVMTDsNCj4gKw0KPiArCW11dGV4X3VubG9jaygm
dGRtX2NvcmVfbG9jayk7DQo+ICsNCj4gKwlyZXR1cm4gYWRhcHRlcjsNCj4gK30NCj4gK0VYUE9S
VF9TWU1CT0wodGRtX2dldF9hZGFwdGVyKTsNCj4gKw0KPiArdm9pZCB0ZG1fcHV0X2FkYXB0ZXIo
c3RydWN0IHRkbV9hZGFwdGVyICphZGFwKSB7DQo+ICsJbW9kdWxlX3B1dChhZGFwLT5vd25lcik7
DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9wdXRfYWRhcHRlcik7DQo+ICsNCj4gKw0KPiAr
LyogUG9ydCBMZXZlbCBBUElzIG9mIFRETSBGcmFtZXdvcmsgKi8gdW5zaWduZWQgaW50IA0KPiAr
dGRtX3BvcnRfb3BlbihzdHJ1Y3QgdGRtX2RyaXZlciAqZHJpdmVyLCB2b2lkICoqaF9wb3J0KSB7
DQo+ICsJc3RydWN0IHRkbV9wb3J0ICpwb3J0Ow0KPiArCXN0cnVjdCB0ZG1fYWRhcHRlciAqYWRh
cDsNCj4gKwl1bnNpZ25lZCBsb25nCQlmbGFnczsNCj4gKwlpbnQgcmVzID0gVERNX0VfT0s7DQo+
ICsNCj4gKwlpZiAoZHJpdmVyID09IE5VTEwpIHsNCj4gKwkJcHJfZXJyKCJkcml2ZXIgTlVMTFxu
Iik7DQo+ICsJCXJldHVybiAtRU5PREVWOw0KPiArCX0NCj4gKwlpZiAoZHJpdmVyLT5hZGFwdGVy
ID09IE5VTEwpIHsNCj4gKwkJcHJfZXJyKCJhZGFwdGVyIE5VTExcbiIpOw0KPiArCQlyZXR1cm4g
LUVOT0RFVjsNCj4gKwl9DQo+ICsNCj4gKwlhZGFwID0gdGRtX2dldF9hZGFwdGVyKGRyaXZlci0+
YWRhcHRlci0+aWQpOw0KPiArCWlmICghYWRhcCkNCj4gKwkJcmV0dXJuIC1FTk9ERVY7DQo+ICsN
Cj4gKwkvKiBUaGlzIGNyZWF0ZXMgYW4gYW5vbnltb3VzIHRkbV9wb3J0LCB3aGljaCBtYXkgbGF0
ZXIgYmUNCj4gKwkgKiBwb2ludGVkIHRvIHNvbWUgc2xvdC4NCj4gKwkgKg0KPiArCSAqLw0KPiAr
CXBvcnQgPSBremFsbG9jKHNpemVvZigqcG9ydCksIEdGUF9LRVJORUwpOw0KPiArCWlmICghcG9y
dCkgew0KPiArCQlyZXMgPSAtRU5PTUVNOw0KPiArCQlnb3RvIG91dDsNCj4gKwl9DQo+ICsNCj4g
Kwlpbml0X3dhaXRxdWV1ZV9oZWFkKCZwb3J0LT5jaF93YWl0X3F1ZXVlKTsNCj4gKw0KPiArDQo+
ICsJcG9ydC0+cnhfbWF4X2ZyYW1lcyA9IE5VTV9TQU1QTEVTX1BFUl9GUkFNRTsNCj4gKwlwb3J0
LT5wb3J0X2NmZy5wb3J0X21vZGUgPSBlX1RETV9QT1JUX0NIQU5ORUxJWkVEOw0KPiArDQo+ICsJ
cG9ydC0+aW5fdXNlID0gMTsNCj4gKw0KPiArCXNucHJpbnRmKGRyaXZlci0+bmFtZSwgVERNX05B
TUVfU0laRSwgInRkbS1kZXYiKTsNCj4gKwlwb3J0LT5kcml2ZXIgPSBkcml2ZXI7DQo+ICsJcG9y
dC0+YWRhcHRlciA9IGFkYXA7DQo+ICsNCj4gKwlzcGluX2xvY2tfaXJxc2F2ZSgmYWRhcC0+cG9y
dGxpc3RfbG9jaywgZmxhZ3MpOw0KPiArCWxpc3RfYWRkX3RhaWwoJnBvcnQtPmxpc3QsICZhZGFw
LT5teXBvcnRzKTsNCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZhZGFwLT5wb3J0bGlzdF9s
b2NrLCBmbGFncyk7DQo+ICsNCj4gKwlJTklUX0xJU1RfSEVBRCgmcG9ydC0+bXljaGFubmVscyk7
DQo+ICsNCj4gKwkqaF9wb3J0ID0gcG9ydDsNCj4gKw0KPiArb3V0Og0KPiArCXJldHVybiByZXM7
DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9wb3J0X29wZW4pOw0KPiArDQo+ICt1bnNpZ25l
ZCBpbnQgdGRtX3BvcnRfY2xvc2Uodm9pZCAqaF9wb3J0KSB7DQo+ICsJc3RydWN0IHRkbV9hZGFw
dGVyICphZGFwOw0KPiArCXN0cnVjdCB0ZG1fZHJpdmVyICpkcml2ZXI7DQo+ICsJc3RydWN0IHRk
bV9wb3J0ICpwb3J0Ow0KPiArCXN0cnVjdCB0ZG1fY2hhbm5lbCAqdGVtcCwgKmNoYW5uZWw7DQo+
ICsJdW5zaWduZWQgbG9uZwkJZmxhZ3M7DQo+ICsJaW50IHJlcyA9IFRETV9FX09LOw0KPiArCXBv
cnQgPSAoc3RydWN0IHRkbV9wb3J0ICopaF9wb3J0Ow0KPiArDQo+ICsJaWYgKHBvcnQgPT0gTlVM
TCkgeyAvKiBpbnZhbGlkIGhhbmRsZSovDQo+ICsJCXByX2VycigiSW52YWxpZCBIYW5kbGUiKTsN
Cj4gKwkJcmV0dXJuIC1FTlhJTzsNCj4gKwl9DQo+ICsNCj4gKwlkcml2ZXIgPSAgcG9ydC0+ZHJp
dmVyOw0KPiArDQo+ICsJaWYgKGRyaXZlciA9PSBOVUxMKSB7DQo+ICsJCXByX2VycigiZHJpdmVy
IE5VTExcbiIpOw0KPiArCQlyZXMgPSAtRU5PREVWOw0KPiArCQlnb3RvIG91dDsNCj4gKwl9DQo+
ICsJaWYgKGRyaXZlci0+YWRhcHRlciA9PSBOVUxMKSB7DQo+ICsJCXByX2VycigiYWRhcHRlciBO
VUxMXG4iKTsNCj4gKwkJcmVzID0gLUVOT0RFVjsNCj4gKwkJZ290byBvdXQ7DQo+ICsJfQ0KPiAr
DQo+ICsJbGlzdF9mb3JfZWFjaF9lbnRyeV9zYWZlKGNoYW5uZWwsIHRlbXAsICZwb3J0LT5teWNo
YW5uZWxzLCBsaXN0KSB7DQo+ICsJaWYgKGNoYW5uZWwpDQo+ICsJCWlmIChjaGFubmVsLT5pbl91
c2UpIHsNCj4gKwkJCXByX2VycigiJXM6IENhbm5vdCBjbG9zZSBwb3J0LiBDaGFubmVsIGluIHVz
ZVxuIiwNCj4gKwkJCQkJCQkJX19mdW5jX18pOw0KPiArCQkJcmVzID0gLUVOWElPOw0KPiArCQkJ
Z290byBvdXQ7DQo+ICsJCQl9DQo+ICsJfQ0KPiArCWFkYXAgPSBkcml2ZXItPmFkYXB0ZXI7DQo+
ICsNCj4gKwlzcGluX2xvY2tfaXJxc2F2ZSgmYWRhcC0+cG9ydGxpc3RfbG9jaywgZmxhZ3MpOw0K
PiArCWxpc3RfZGVsKCZwb3J0LT5saXN0KTsNCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZh
ZGFwLT5wb3J0bGlzdF9sb2NrLCBmbGFncyk7DQo+ICsNCj4gKwlpZiAocG9ydC0+cF9wb3J0X2Rh
dGEgIT0gTlVMTCkgew0KPiArCQlpbnQgaTsNCj4gKwkJc3RydWN0IHRkbV9iZCAqY2hfYmQ7DQo+
ICsNCj4gKwkJLyogSWYgdGhlIHRkbSBpcyBpbiBjaGFubmVsaXNlZCBtb2RlLA0KPiArCQlkZS1h
bGxvY2F0ZSB0aGUgY2hhbm5lbGlzZWQgYnVmZmVyICovDQo+ICsJCWNoX2JkID0gJihwb3J0LT5w
X3BvcnRfZGF0YS0+cnhfZGF0YV9maWZvWzBdKTsNCj4gKwkJZm9yIChpID0gMDsgY2hfYmQgJiYg
aSA8IFRETV9DSF9SWF9CRF9SSU5HX1NJWkU7IGkrKykgew0KPiArCQkJY2hfYmQtPmZsYWcgPSAw
Ow0KPiArCQkJY2hfYmQrKzsNCj4gKwkJfQ0KPiArCQljaF9iZCA9ICYocG9ydC0+cF9wb3J0X2Rh
dGEtPnR4X2RhdGFfZmlmb1swXSk7DQo+ICsJCWZvciAoaSA9IDA7IGNoX2JkICYmIGkgPCBURE1f
Q0hfVFhfQkRfUklOR19TSVpFOyBpKyspIHsNCj4gKwkJCWNoX2JkLT5mbGFnID0gMDsNCj4gKwkJ
CWNoX2JkKys7DQo+ICsJCX0NCj4gKwkJa2ZyZWUocG9ydC0+cF9wb3J0X2RhdGEpOw0KPiArCX0N
Cj4gKwlrZnJlZShwb3J0KTsNCj4gKwlyZXR1cm4gcmVzOw0KPiArb3V0Og0KPiArCWlmIChwb3J0
KQ0KPiArCQlrZnJlZShwb3J0LT5wX3BvcnRfZGF0YSk7DQo+ICsJa2ZyZWUocG9ydCk7DQo+ICsJ
cmV0dXJuIHJlczsNCj4gK30NCj4gK0VYUE9SVF9TWU1CT0wodGRtX3BvcnRfY2xvc2UpOw0KPiAr
DQo+ICt1bnNpZ25lZCBpbnQgdGRtX2NoYW5uZWxfcmVhZCh2b2lkICpoX3BvcnQsIHZvaWQgKmhf
Y2hhbm5lbCwNCj4gKwkJCQl2b2lkICpwX2RhdGEsIHUxNiAqc2l6ZSkNCj4gK3sNCj4gKwlzdHJ1
Y3QgdGRtX3BvcnQgKnBvcnQ7DQo+ICsJc3RydWN0IHRkbV9jaGFubmVsICpjaGFubmVsOw0KPiAr
CXN0cnVjdCB0ZG1fYmQgKnJ4X2JkOw0KPiArCXVuc2lnbmVkIGxvbmcgZmxhZ3M7DQo+ICsJaW50
IGksIHJlcyA9IFRETV9FX09LOw0KPiArCXVuc2lnbmVkIHNob3J0ICpidWYsICpidWYxOw0KPiAr
CXBvcnQgPSAoc3RydWN0IHRkbV9wb3J0ICopaF9wb3J0Ow0KPiArCWNoYW5uZWwgPSAoc3RydWN0
IHRkbV9jaGFubmVsICopaF9jaGFubmVsOw0KPiArDQo+ICsJaWYgKChwb3J0ICYmIGNoYW5uZWwp
ID09IDApIHsgLyogaW52YWxpZCBoYW5kbGUqLw0KPiArCQlwcl9lcnIoIiVzOkludmFsaWQgSGFu
ZGxlXG4iLCBfX2Z1bmNfXyk7DQo+ICsJCXJldHVybiAtRU5YSU87DQo+ICsJfQ0KPiArDQo+ICsJ
aWYgKCFwb3J0LT5pbl91c2UpDQo+ICsJCXJldHVybiAtRUlPOw0KPiArCWlmICghY2hhbm5lbC0+
cF9jaF9kYXRhIHx8ICFjaGFubmVsLT5pbl91c2UpDQo+ICsJCXJldHVybiAtRUlPOw0KPiArDQo+
ICsJc3Bpbl9sb2NrX2lycXNhdmUoJmNoYW5uZWwtPnBfY2hfZGF0YS0+cnhfY2hhbm5lbF9sb2Nr
LCBmbGFncyk7DQo+ICsJcnhfYmQgPSBjaGFubmVsLT5wX2NoX2RhdGEtPnJ4X291dF9kYXRhOw0K
PiArDQo+ICsJaWYgKHJ4X2JkLT5mbGFnKSB7DQo+ICsJCSpzaXplID0gcnhfYmQtPmxlbmd0aDsN
Cj4gKwkJYnVmID0gKHUxNiAqKSBwX2RhdGE7DQo+ICsJCWJ1ZjEgPSAodTE2ICopcnhfYmQtPnBf
ZGF0YTsNCj4gKwkJZm9yIChpID0gMDsgaSA8IE5VTV9TQU1QTEVTX1BFUl9GUkFNRTsgaSsrKQ0K
PiArCQkJYnVmW2ldID0gYnVmMVtpXTsNCj4gKwkJcnhfYmQtPmZsYWcgPSAwOw0KPiArCQlyeF9i
ZC0+b2Zmc2V0ID0gMDsNCj4gKwkJY2hhbm5lbC0+cF9jaF9kYXRhLT5yeF9vdXRfZGF0YSA9IChy
eF9iZC0+d3JhcCkgPw0KPiArCQkJCWNoYW5uZWwtPnBfY2hfZGF0YS0+cnhfZGF0YV9maWZvIDog
cnhfYmQgKyAxOw0KPiArDQo+ICsJfSBlbHNlIHsNCj4gKwkJc3Bpbl91bmxvY2tfaXJxcmVzdG9y
ZSgmY2hhbm5lbC0+cF9jaF9kYXRhLT5yeF9jaGFubmVsX2xvY2ssDQo+ICsJCQkJCQlmbGFncyk7
DQo+ICsJCXByX2luZm8oIk5vIERhdGEgQXZhaWxhYmxlIik7DQo+ICsJCXJldHVybiAtRUFHQUlO
Ow0KPiArCX0NCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZjaGFubmVsLT5wX2NoX2RhdGEt
PnJ4X2NoYW5uZWxfbG9jaywgZmxhZ3MpOw0KPiArDQo+ICsJcmV0dXJuIHJlczsNCj4gK30NCj4g
K0VYUE9SVF9TWU1CT0wodGRtX2NoYW5uZWxfcmVhZCk7DQo+ICsNCj4gKw0KPiArdW5zaWduZWQg
aW50IHRkbV9jaGFubmVsX3dyaXRlKHZvaWQgKmhfcG9ydCwgdm9pZCAqaF9jaGFubmVsLA0KPiAr
CQkJCXZvaWQgKnBfZGF0YSwgdTE2IHNpemUpDQo+ICt7DQo+ICsJc3RydWN0IHRkbV9wb3J0ICpw
b3J0Ow0KPiArCXN0cnVjdCB0ZG1fY2hhbm5lbCAqY2hhbm5lbDsNCj4gKwlzdHJ1Y3QgdGRtX2Jk
ICp0eF9iZDsNCj4gKwl1bnNpZ25lZCBsb25nIGZsYWdzOw0KPiArCWludCBlcnIgPSBURE1fRV9P
SzsNCj4gKwlwb3J0ID0gKHN0cnVjdCB0ZG1fcG9ydCAqKWhfcG9ydDsNCj4gKwljaGFubmVsID0g
KHN0cnVjdCB0ZG1fY2hhbm5lbCAqKWhfY2hhbm5lbDsgI2lmZGVmIFRETV9DT1JFX0RFQlVHDQo+
ICsJYm9vbCBkYXRhX2ZsYWcgPSAwOw0KPiArI2VuZGlmDQo+ICsNCj4gKwlpZiAoKHBvcnQgJiYg
Y2hhbm5lbCkgPT0gMCkgeyAvKiBpbnZhbGlkIGhhbmRsZSovDQo+ICsJCXByX2VycigiSW52YWxp
ZCBIYW5kbGUiKTsNCj4gKwkJcmV0dXJuIC1FTlhJTzsNCj4gKwl9DQo+ICsNCj4gKwlpZiAocF9k
YXRhID09IE5VTEwpIHsgLyogaW52YWxpZCBkYXRhKi8NCj4gKwkJcHJfZXJyKCJJbnZhbGlkIERh
dGEiKTsNCj4gKwkJcmV0dXJuIC1FRkFVTFQ7DQo+ICsJfQ0KPiArDQo+ICsJaWYgKCFwb3J0LT5p
bl91c2UpDQo+ICsJCXJldHVybiAtRUlPOw0KPiArCWlmICghY2hhbm5lbC0+cF9jaF9kYXRhIHx8
ICFjaGFubmVsLT5pbl91c2UpDQo+ICsJCXJldHVybiAtRUlPOw0KPiArDQo+ICsJc3Bpbl9sb2Nr
X2lycXNhdmUoJmNoYW5uZWwtPnBfY2hfZGF0YS0+dHhfY2hhbm5lbF9sb2NrLCBmbGFncyk7DQo+
ICsJdHhfYmQgPSBjaGFubmVsLT5wX2NoX2RhdGEtPnR4X2luX2RhdGE7DQo+ICsNCj4gKwlpZiAo
IXR4X2JkLT5mbGFnKSB7DQo+ICsJCXR4X2JkLT5sZW5ndGggPSBzaXplOw0KPiArCQltZW1jcHko
dHhfYmQtPnBfZGF0YSwgcF9kYXRhLA0KPiArCQkJc2l6ZSAqIHBvcnQtPmFkYXB0ZXItPmFkYXB0
X2NmZy5zbG90X3dpZHRoKTsNCj4gKwkJdHhfYmQtPmZsYWcgPSAxOw0KPiArCQl0eF9iZC0+b2Zm
c2V0ID0gMDsNCj4gKwkJY2hhbm5lbC0+cF9jaF9kYXRhLT50eF9pbl9kYXRhID0gKHR4X2JkLT53
cmFwKSA/DQo+ICsJCQkJY2hhbm5lbC0+cF9jaF9kYXRhLT50eF9kYXRhX2ZpZm8gOiB0eF9iZCsx
Ow0KPiArCQlwb3J0LT5wb3J0X3N0YXQudHhfcGt0X2NvdW50Kys7DQo+ICsjaWZkZWYgVERNX0NP
UkVfREVCVUcNCj4gKwkJZGF0YV9mbGFnID0gMTsNCj4gKyNlbmRpZg0KPiArCX0gZWxzZSB7DQo+
ICsJCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmNoYW5uZWwtPnBfY2hfZGF0YS0+dHhfY2hhbm5l
bF9sb2NrLA0KPiArCQkJCQkJZmxhZ3MpOw0KPiArCQlwb3J0LT5wb3J0X3N0YXQudHhfcGt0X2Ry
b3BfY291bnQrKzsNCj4gKwkJcHJfZXJyKCJFX05PX01FTU9SWSAtRmFpbGVkIFRyYW5zbWl0Iik7
DQo+ICsJCXJldHVybiAtRU5PTUVNOw0KPiArCX0NCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3Jl
KCZjaGFubmVsLT5wX2NoX2RhdGEtPnR4X2NoYW5uZWxfbG9jaywgZmxhZ3MpOw0KPiArDQo+ICsj
aWZkZWYJVERNX0NPUkVfREVCVUcNCj4gKwlpZiAoZGF0YV9mbGFnKSB7DQo+ICsJCWludCBrOw0K
PiArCQlwcl9pbmZvKCJcblRYIHBvcnQ6JWQgLSBXcml0ZSAtIFBvcnQgVFgtJWRcbiIsDQo+ICsJ
CQkJCQlwb3J0LT5wb3J0X2lkLCBzaXplKTsNCj4gKwkJZm9yIChrID0gMDsgayA8IHNpemU7IGsr
KykNCj4gKwkJCXByX2luZm8oIiV4IiwgcF9kYXRhW2tdKTsNCj4gKwkJcHJfaW5mbygiXG4iKTsN
Cj4gKwl9DQo+ICsjZW5kaWYNCj4gKwlyZXR1cm4gZXJyOw0KPiArfQ0KPiArRVhQT1JUX1NZTUJP
TCh0ZG1fY2hhbm5lbF93cml0ZSk7DQo+ICsNCj4gK3dhaXRfcXVldWVfaGVhZF90ICp0ZG1fcG9y
dF9nZXRfd2FpdF9xdWV1ZSh2b2lkICAqaF9wb3J0KSB7DQo+ICsJc3RydWN0IHRkbV9wb3J0ICpw
b3J0Ow0KPiArCXBvcnQgPSAoc3RydWN0IHRkbV9wb3J0ICopaF9wb3J0Ow0KPiArDQo+ICsJaWYg
KHBvcnQgPT0gTlVMTCkgeyAvKiBpbnZhbGlkIGhhbmRsZSovDQo+ICsJCXByX2VycigiSW52YWxp
ZCBIYW5kbGUiKTsNCj4gKwkJcmV0dXJuIE5VTEw7DQo+ICsJfQ0KPiArDQo+ICsJcmV0dXJuICZw
b3J0LT5jaF93YWl0X3F1ZXVlOw0KPiArDQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9wb3J0
X2dldF93YWl0X3F1ZXVlKTsNCj4gKw0KPiArLyogRHJpdmVyIEZ1bmN0aW9uIGZvciBzZWxlY3Qg
YW5kIHBvbGwuIEJhc2VkIG9uIFBvcnQgbm8sIGl0IHNsZWVwcyANCj4gK29uDQo+ICsgKiB3YWl0
cXVldWUgKi8NCj4gK3Vuc2lnbmVkIGludCB0ZG1fcG9ydF9wb2xsKHZvaWQgKmhfcG9ydCwgdW5z
aWduZWQgaW50IHdhaXRfdGltZSkgew0KPiArCXN0cnVjdCB0ZG1fcG9ydCAqcG9ydDsNCj4gKwl1
bnNpZ25lZCBsb25nIHRpbWVvdXQgPSBtc2Vjc190b19qaWZmaWVzKHdhaXRfdGltZSk7DQo+ICsJ
cG9ydCA9IChzdHJ1Y3QgdGRtX3BvcnQgKiloX3BvcnQ7DQo+ICsNCj4gKwlpZiAocG9ydCA9PSBO
VUxMKSB7IC8qIGludmFsaWQgaGFuZGxlKi8NCj4gKwkJcHJfZXJyKCIlczogSW52YWxpZCBIYW5k
bGVcbiIsIF9fZnVuY19fKTsNCj4gKwkJcmV0dXJuIC1FTlhJTzsNCj4gKwl9DQo+ICsJaWYgKCFw
b3J0LT5wX3BvcnRfZGF0YSB8fCAhcG9ydC0+aW5fdXNlKQ0KPiArCQlyZXR1cm4gLUVJTzsNCj4g
Kw0KPiArCWlmIChwb3J0LT5wX3BvcnRfZGF0YS0+cnhfb3V0X2RhdGEtPmZsYWcpIHsNCj4gKwkJ
cHJfZGVidWcoIkRhdGEgQXZhaWxhYmxlIik7DQo+ICsJCXJldHVybiBURE1fRV9PSzsNCj4gKwl9
DQo+ICsJaWYgKHRpbWVvdXQpIHsNCj4gKwkJd2FpdF9ldmVudF9pbnRlcnJ1cHRpYmxlX3RpbWVv
dXQocG9ydC0+Y2hfd2FpdF9xdWV1ZSwNCj4gKwkJCQkJICBwb3J0LT5wX3BvcnRfZGF0YS0+cnhf
b3V0X2RhdGEtPmZsYWcsDQo+ICsJCQkJCSAgdGltZW91dCk7DQo+ICsNCj4gKwkJaWYgKHBvcnQt
PnBfcG9ydF9kYXRhLT5yeF9vdXRfZGF0YS0+ZmxhZykgew0KPiArCQkJcHJfZGVidWcoIkRhdGEg
QXZhaWxhYmxlIik7DQo+ICsJCQlyZXR1cm4gVERNX0VfT0s7DQo+ICsJCX0NCj4gKwl9DQo+ICsJ
cmV0dXJuIC1FQUdBSU47DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9wb3J0X3BvbGwpOw0K
PiArDQo+ICt1bnNpZ25lZCBpbnQgdGRtX3BvcnRfZ2V0X3N0YXRzKHZvaWQgKmhfcG9ydCwgc3Ry
dWN0IHRkbV9wb3J0X3N0YXRzIA0KPiArKnBvcnRTdGF0KSB7DQo+ICsJc3RydWN0IHRkbV9wb3J0
ICpwb3J0Ow0KPiArCWludCBwb3J0X251bTsNCj4gKwlwb3J0ID0gKHN0cnVjdCB0ZG1fcG9ydCAq
KWhfcG9ydDsNCj4gKw0KPiArCWlmIChwb3J0ID09IE5VTEwgfHwgcG9ydFN0YXQgPT0gTlVMTCkg
eyAvKiBpbnZhbGlkIGhhbmRsZSovDQo+ICsJCXByX2VycigiSW52YWxpZCBIYW5kbGUiKTsNCj4g
KwkJcmV0dXJuIC1FTlhJTzsNCj4gKwl9DQo+ICsJcG9ydF9udW0gPSAgcG9ydC0+cG9ydF9pZDsN
Cj4gKw0KPiArCW1lbWNweShwb3J0U3RhdCwgJnBvcnQtPnBvcnRfc3RhdCwgc2l6ZW9mKHN0cnVj
dCB0ZG1fcG9ydF9zdGF0cykpOw0KPiArDQo+ICsJcHJfaW5mbygiVERNIFBvcnQgJWQgR2V0IFN0
YXRzIiwgcG9ydF9udW0pOw0KPiArDQo+ICsJcmV0dXJuIFRETV9FX09LOw0KPiArfQ0KPiArRVhQ
T1JUX1NZTUJPTCh0ZG1fcG9ydF9nZXRfc3RhdHMpOw0KPiArDQo+ICsvKiBEYXRhIGhhbmRsaW5n
IGZ1bmN0aW9ucyAqLw0KPiArDQo+ICtzdGF0aWMgaW50IHRkbV9kYXRhX3J4X2RlaW50ZXJsZWF2
ZShzdHJ1Y3QgdGRtX2FkYXB0ZXIgKmFkYXApIHsNCj4gKwlzdHJ1Y3QgdGRtX3BvcnQgKnBvcnQs
ICpuZXh0Ow0KPiArCXN0cnVjdCB0ZG1fY2hhbm5lbCAqY2hhbm5lbCwgKnRlbXA7DQo+ICsJc3Ry
dWN0IHRkbV9iZAkqY2hfYmQ7DQo+ICsNCj4gKwlpbnQgaSwgYnVmX3NpemUsIGNoX2RhdGFfbGVu
Ow0KPiArCXUxNiAqaW5wdXRfdGRtX2J1ZmZlcjsNCj4gKwl1MTYgKnBjbV9idWZmZXI7DQo+ICsJ
aW50IHNsb3Rfd2lkdGg7DQo+ICsJaW50IGZyYW1lX2NoX2RhdGFfc2l6ZTsNCj4gKwlib29sIGNo
X2RhdGE7DQo+ICsJaW50IGJ5dGVzX2luX2ZpZm9fcGVyX2ZyYW1lOw0KPiArCWludCBieXRlc19z
bG90X29mZnNldDsNCj4gKw0KPiArCWNoX2RhdGFfbGVuID0gTlVNX1NBTVBMRVNfUEVSX0ZSQU1F
Ow0KPiArCWZyYW1lX2NoX2RhdGFfc2l6ZSA9IE5VTV9TQU1QTEVTX1BFUl9GUkFNRTsNCj4gKwlj
aF9kYXRhID0gMDsNCj4gKw0KPiArCWlmICghYWRhcCkgeyAvKiBpbnZhbGlkIGhhbmRsZSovDQo+
ICsJCXByX2VycigiJXM6IEludmFsaWQgSGFuZGxlXG4iLCBfX2Z1bmNfXyk7DQo+ICsJCXJldHVy
biAtRU5YSU87DQo+ICsJfQ0KPiArDQo+ICsJc2xvdF93aWR0aCA9IGFkYXAtPmFkYXB0X2NmZy5z
bG90X3dpZHRoOw0KPiArCWJ1Zl9zaXplID0gdGRtX2FkYXBfcmVjdihhZGFwLCAodm9pZCAqKikm
aW5wdXRfdGRtX2J1ZmZlcik7DQo+ICsJaWYgKGJ1Zl9zaXplIDw9IDAgfHwgIWlucHV0X3RkbV9i
dWZmZXIpDQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiArDQo+ICsJYnl0ZXNfaW5fZmlmb19wZXJf
ZnJhbWUgPSBidWZfc2l6ZS9mcmFtZV9jaF9kYXRhX3NpemU7DQo+ICsJYnl0ZXNfc2xvdF9vZmZz
ZXQgPSBieXRlc19pbl9maWZvX3Blcl9mcmFtZS9zbG90X3dpZHRoOw0KPiArDQo+ICsJLyogZGUt
aW50ZXJsZWF2aW5nIGZvciBhbGwgcG9ydHMqLw0KPiArCWxpc3RfZm9yX2VhY2hfZW50cnlfc2Fm
ZShwb3J0LCBuZXh0LCAmYWRhcC0+bXlwb3J0cywgbGlzdCkgew0KPiArDQo+ICsJCS8qIGlmIHRo
ZSBwb3J0IGlzIG5vdCBvcGVuICovDQo+ICsJCWlmICghcG9ydC0+aW5fdXNlKQ0KPiArCQkJY29u
dGludWU7DQo+ICsNCj4gKwkJbGlzdF9mb3JfZWFjaF9lbnRyeV9zYWZlKGNoYW5uZWwsIHRlbXAs
ICZwb3J0LT5teWNoYW5uZWxzLA0KPiArCQkJCQkJCWxpc3QpIHsNCj4gKwkJLyogaWYgdGhlIGNo
YW5uZWwgaXMgbm90IG9wZW4gKi8NCj4gKwkJaWYgKCFjaGFubmVsLT5pbl91c2UgfHwgIWNoYW5u
ZWwtPnBfY2hfZGF0YSkNCj4gKwkJCWNvbnRpbnVlOw0KPiArCQljaF9iZCA9IGNoYW5uZWwtPnBf
Y2hfZGF0YS0+cnhfaW5fZGF0YTsNCj4gKwkJc3Bpbl9sb2NrKCZjaGFubmVsLT5wX2NoX2RhdGEt
PnJ4X2NoYW5uZWxfbG9jayk7DQo+ICsJCQkvKmlmIG9sZCBkYXRhIGlzIHRvIGJlIGRpc2NhcmRl
ZCAqLw0KPiArCQlpZiAodXNlX2xhdGVzdF90ZG1fZGF0YSkNCj4gKwkJCWlmIChjaF9iZC0+Zmxh
Zykgew0KPiArCQkJCWNoX2JkLT5mbGFnID0gMDsNCj4gKwkJCQljaF9iZC0+b2Zmc2V0ID0gMDsN
Cj4gKwkJCQlpZiAoY2hfYmQgPT0gY2hhbm5lbC0+cF9jaF9kYXRhLT5yeF9vdXRfZGF0YSkNCj4g
KwkJCQkJY2hhbm5lbC0+cF9jaF9kYXRhLT5yeF9vdXRfZGF0YSA9DQo+ICsJCQkJCQljaF9iZC0+
d3JhcCA/DQo+ICsJCQkJCQljaGFubmVsLT5wX2NoX2RhdGEtPnJ4X2RhdGFfZmlmbw0KPiArCQkJ
CQkJOiBjaF9iZCsxOw0KPiArCQkJCQlwb3J0LT5wb3J0X3N0YXQucnhfcGt0X2Ryb3BfY291bnQr
KzsNCj4gKwkJCQl9DQo+ICsJCQkvKiBpZiB0aGUgYmQgaXMgZW1wdHkgKi8NCj4gKwkJCWlmICgh
Y2hfYmQtPmZsYWcpIHsNCj4gKwkJCQlpZiAoY2hfYmQtPm9mZnNldCA9PSAwKQ0KPiArCQkJCQlj
aF9iZC0+bGVuZ3RoID0gcG9ydC0+cnhfbWF4X2ZyYW1lczsNCj4gKw0KPiArCQkJCXBjbV9idWZm
ZXIgPSBjaF9iZC0+cF9kYXRhICsgY2hfYmQtPm9mZnNldDsNCj4gKwkJCQkvKiBEZS1pbnRlcmxl
YXZpbmcgdGhlIGRhdGEgKi8NCj4gKwkJCQlmb3IgKGkgPSAwOyBpIDwgY2hfZGF0YV9sZW47IGkr
Kykgew0KPiArCQkJCQlwY21fYnVmZmVyW2ldDQo+ICsJCQkJCT0gaW5wdXRfdGRtX2J1ZmZlcltp
KmJ5dGVzX3Nsb3Rfb2Zmc2V0ICsNCj4gKwkJCQkJCWNoYW5uZWwtPmNoX2lkXTsNCj4gKwkJCQl9
DQo+ICsJCQkJY2hfYmQtPm9mZnNldCArPSBjaF9kYXRhX2xlbiAqIHNsb3Rfd2lkdGg7DQo+ICsN
Cj4gKwkJCQlpZiAoY2hfYmQtPm9mZnNldCA+PQ0KPiArCQkJCQkoY2hfYmQtPmxlbmd0aCAtIGZy
YW1lX2NoX2RhdGFfc2l6ZSkqDQo+ICsJCQkJCQkoYWRhcC0+YWRhcHRfY2ZnLnNsb3Rfd2lkdGgp
KSB7DQo+ICsJCQkJCWNoX2JkLT5mbGFnID0gMTsNCj4gKwkJCQkJY2hfYmQtPm9mZnNldCA9IDA7
DQo+ICsJCQkJCWNoYW5uZWwtPnBfY2hfZGF0YS0+cnhfaW5fZGF0YSA9DQo+ICsJCQkJCQljaF9i
ZC0+d3JhcCA/DQo+ICsJCQkJCQljaGFubmVsLT5wX2NoX2RhdGEtPnJ4X2RhdGFfZmlmbw0KPiAr
CQkJCQkJOiBjaF9iZCsxOw0KPiArCQkJCQljaF9kYXRhID0gMTsNCj4gKwkJCQl9DQo+ICsJCQl9
IGVsc2Ugew0KPiArCQkJCXBvcnQtPnBvcnRfc3RhdC5yeF9wa3RfZHJvcF9jb3VudCsrOw0KPiAr
CQkJfQ0KPiArCQlzcGluX3VubG9jaygmY2hhbm5lbC0+cF9jaF9kYXRhLT5yeF9jaGFubmVsX2xv
Y2spOw0KPiArCQl9DQo+ICsNCj4gKwkJaWYgKGNoX2RhdGEpIHsNCj4gKwkJCS8qCVdha2UgdXAg
dGhlIFBvcnQgRGF0YSBQb2xsIGV2ZW50ICovDQo+ICsJCQl3YWtlX3VwX2ludGVycnVwdGlibGUo
JnBvcnQtPmNoX3dhaXRfcXVldWUpOw0KPiArI2lmZGVmCVRETV9DT1JFX0RFQlVHDQo+ICsJCQlw
cl9pbmZvKCJQb3J0IFJYLSVkLSVkXG4iLCBjaGFubmVsLT5jaF9pZCwgY2hfZGF0YV9sZW4pOw0K
PiArCQkJZm9yIChpID0gMDsgaSA8IGNoX2RhdGFfbGVuOyBpKyspDQo+ICsJCQkJcHJfaW5mbygi
JXgiLCBwY21fYnVmZmVyW2ldKTsNCj4gKwkJCXByX2luZm8oIlxuIik7DQo+ICsjZW5kaWYNCj4g
KwkJCXBvcnQtPnBvcnRfc3RhdC5yeF9wa3RfY291bnQrKzsNCj4gKwkJCWNoX2RhdGEgPSAwOw0K
PiArCQl9DQo+ICsJfQ0KPiArCXJldHVybiBURE1fRV9PSzsNCj4gK30NCj4gKw0KPiArc3RhdGlj
IGludCB0ZG1fZGF0YV90eF9pbnRlcmxlYXZlKHN0cnVjdCB0ZG1fYWRhcHRlciAqYWRhcCkgew0K
PiArCXN0cnVjdCB0ZG1fcG9ydCAqcG9ydCwgKm5leHQ7DQo+ICsJc3RydWN0IHRkbV9jaGFubmVs
ICpjaGFubmVsLCAqdGVtcDsNCj4gKwlzdHJ1Y3QgdGRtX2JkCSpjaF9iZDsNCj4gKwlpbnQgaSwg
YnVmX3NpemUsIGNoX2RhdGFfbGVuID0gTlVNX1NBTVBMRVNfUEVSX0ZSQU1FOw0KPiArCWJvb2wg
bGFzdF9kYXRhID0gMDsNCj4gKwl1MTYgKm91dHB1dF90ZG1fYnVmZmVyOw0KPiArCXUxNiAqcGNt
X2J1ZmZlcjsNCj4gKwlpbnQgZnJhbWVfY2hfZGF0YV9zaXplID0gTlVNX1NBTVBMRVNfUEVSX0ZS
QU1FOw0KPiArCWludCBieXRlc19pbl9maWZvX3Blcl9mcmFtZTsNCj4gKwlpbnQgYnl0ZXNfc2xv
dF9vZmZzZXQ7DQo+ICsNCj4gKyNpZmRlZiBURE1fQ09SRV9ERUJVRw0KPiArCXU4CWRhdGFfZmxh
ZyA9IDA7DQo+ICsjZW5kaWYNCj4gKw0KPiArCWlmIChhZGFwID09IE5VTEwpIHsgLyogaW52YWxp
ZCBoYW5kbGUqLw0KPiArCQlwcl9lcnIoIiVzOiBJbnZhbGlkIEhhbmRsZVxuIiwgX19mdW5jX18p
Ow0KPiArCQlyZXR1cm4gLUVOWElPOw0KPiArCX0NCj4gKw0KPiArCWJ1Zl9zaXplID0gdGRtX2Fk
YXBfZ2V0X3dyaXRlX2J1ZihhZGFwLCAodm9pZCAqKikmb3V0cHV0X3RkbV9idWZmZXIpOw0KPiAr
CWlmIChidWZfc2l6ZSA8PSAwIHx8ICFvdXRwdXRfdGRtX2J1ZmZlcikNCj4gKwkJcmV0dXJuIC1F
SU5WQUw7DQo+ICsNCj4gKwlieXRlc19pbl9maWZvX3Blcl9mcmFtZSA9IGJ1Zl9zaXplL2ZyYW1l
X2NoX2RhdGFfc2l6ZTsNCj4gKwlieXRlc19zbG90X29mZnNldCA9IA0KPiArYnl0ZXNfaW5fZmlm
b19wZXJfZnJhbWUvYWRhcC0+YWRhcHRfY2ZnLnNsb3Rfd2lkdGg7DQo+ICsNCj4gKw0KPiArCW1l
bXNldChvdXRwdXRfdGRtX2J1ZmZlciwgMCwgc2l6ZW9mKGJ1Zl9zaXplKSk7DQo+ICsNCj4gKwls
aXN0X2Zvcl9lYWNoX2VudHJ5X3NhZmUocG9ydCwgbmV4dCwgJmFkYXAtPm15cG9ydHMsIGxpc3Qp
IHsNCj4gKw0KPiArCQkvKiBjaGVjayBpZiB0aGUgcG9ydCBpcyBvcGVuICovDQo+ICsJCWlmICgh
cG9ydC0+aW5fdXNlKQ0KPiArCQkJY29udGludWU7DQo+ICsNCj4gKwkJbGlzdF9mb3JfZWFjaF9l
bnRyeV9zYWZlKGNoYW5uZWwsIHRlbXAsICZwb3J0LT5teWNoYW5uZWxzLA0KPiArCQkJCQkJCQls
aXN0KSB7DQo+ICsJCXByX2RlYnVnKCJUWC1UZG0gJWQgKHNsb3RzLSkiLCBjaGFubmVsLT5jaF9p
ZCk7DQo+ICsNCj4gKw0KPiArCQkvKiBpZiB0aGUgY2hhbm5lbCBpcyBvcGVuICovDQo+ICsJCWlm
ICghY2hhbm5lbC0+aW5fdXNlIHx8ICFjaGFubmVsLT5wX2NoX2RhdGEpDQo+ICsJCQljb250aW51
ZTsNCj4gKw0KPiArCQlzcGluX2xvY2soJmNoYW5uZWwtPnBfY2hfZGF0YS0+dHhfY2hhbm5lbF9s
b2NrKTsNCj4gKwkJaWYgKCFjaGFubmVsLT5pbl91c2UgfHwgIWNoYW5uZWwtPnBfY2hfZGF0YSkN
Cj4gKwkJCWNvbnRpbnVlOw0KPiArCQkJY2hfYmQgPSBjaGFubmVsLT5wX2NoX2RhdGEtPnR4X291
dF9kYXRhOw0KPiArCQkJaWYgKGNoX2JkLT5mbGFnKSB7DQo+ICsJCQkJcGNtX2J1ZmZlciA9ICh1
MTYgKikoKHVpbnQ4X3QgKiljaF9iZC0+cF9kYXRhICsNCj4gKwkJCQkJCWNoX2JkLT5vZmZzZXQp
Ow0KPiArCQkJCS8qaWYgdGhlIGJ1ZmZlciBoYXMgbGVzcyBmcmFtZXMgdGhhbiByZXF1aXJlZCAq
Lw0KPiArCQkJCWlmIChmcmFtZV9jaF9kYXRhX3NpemUgPj0NCj4gKwkJCQkJKChjaF9iZC0+bGVu
Z3RoKSAtIChjaF9iZC0+b2Zmc2V0Lw0KPiArCQkJCQkJYWRhcC0+YWRhcHRfY2ZnLnNsb3Rfd2lk
dGgpKSkgew0KPiArCQkJCQljaF9kYXRhX2xlbiA9DQo+ICsJCQkJCShjaF9iZC0+bGVuZ3RoKSAt
IChjaF9iZC0+b2Zmc2V0Lw0KPiArCQkJCQkJYWRhcC0+YWRhcHRfY2ZnLnNsb3Rfd2lkdGgpOw0K
PiArCQkJCQlsYXN0X2RhdGEgPSAxOw0KPiArCQkJCX0gZWxzZSB7DQo+ICsJCQkJCWNoX2RhdGFf
bGVuID0gZnJhbWVfY2hfZGF0YV9zaXplOw0KPiArCQkJCX0NCj4gKwkJCQkvKiBJbnRlcmxlYXZp
bmcgdGhlIGRhdGEgKi8NCj4gKwkJCQlmb3IgKGkgPSAwOyBpIDwgY2hfZGF0YV9sZW47IGkrKykg
ew0KPiArCQkJCQkvKiBUT0RPLSBuZWVkIHRvIGJlIGdlbmVyaWMgZm9yIGFueSBzaXplDQo+ICsJ
CQkJCSAgIGFzc2lnbm1lbnQqLw0KPiArCQkJCQlvdXRwdXRfdGRtX2J1ZmZlcltjaGFubmVsLT5j
aF9pZCArDQo+ICsJCQkJCQlieXRlc19zbG90X29mZnNldCAqIGldID0NCj4gKwkJCQkJCQkJcGNt
X2J1ZmZlcltpXTsNCj4gKwkJCQl9DQo+ICsJCQkJLyogSWYgYWxsIHRoZSBkYXRhIG9mIHRoaXMg
YnVmZmVyIGlzDQo+ICsJCQkJCQkJdHJhbnNtaXR0ZWQgKi8NCj4gKwkJCQlpZiAobGFzdF9kYXRh
KSB7DQo+ICsJCQkJCWNoX2JkLT5mbGFnID0gMDsNCj4gKwkJCQkJY2hfYmQtPm9mZnNldCA9IDA7
DQo+ICsJCQkJCWNoYW5uZWwtPnBfY2hfZGF0YS0+dHhfb3V0X2RhdGEgPQ0KPiArCQkJCQkJY2hf
YmQtPndyYXAgPw0KPiArCQkJCQkJY2hhbm5lbC0+cF9jaF9kYXRhLT50eF9kYXRhX2ZpZm8NCj4g
KwkJCQkJCTogY2hfYmQrMTsNCj4gKwkJCQkJcG9ydC0+cG9ydF9zdGF0LnR4X3BrdF9jb25mX2Nv
dW50Kys7DQo+ICsJCQkJfSBlbHNlIHsNCj4gKwkJCQkJY2hfYmQtPm9mZnNldCArPSBjaF9kYXRh
X2xlbiAqDQo+ICsJCQkJCQkoYWRhcC0+YWRhcHRfY2ZnLnNsb3Rfd2lkdGgpOw0KPiArCQkJCX0N
Cj4gKyNpZmRlZglURE1fQ09SRV9ERUJVRw0KPiArCQkJCWRhdGFfZmxhZyA9IDE7DQo+ICsjZW5k
aWYNCj4gKwkJCX0NCj4gKwkJc3Bpbl91bmxvY2soJmNoYW5uZWwtPnBfY2hfZGF0YS0+dHhfY2hh
bm5lbF9sb2NrKTsNCj4gKwkJfQ0KPiArCX0NCj4gKw0KPiArI2lmZGVmCVRETV9DT1JFX0RFQlVH
DQo+ICsJaWYgKGRhdGFfZmxhZykgew0KPiArCQlwcl9pbmZvKCJUWC1URE0gSW50ZXJsZWF2ZWQg
RGF0YS1cbiIpOw0KPiArCQlmb3IgKGkgPSAwOyBpIDwgNjQ7IGkrKykNCj4gKwkJCXByX2luZm8o
IiV4Iiwgb3V0cHV0X3RkbV9idWZmZXJbaV0pOw0KPiArCQlwcl9pbmZvKCJcbiIpOw0KPiArCSAg
fQ0KPiArI2VuZGlmDQo+ICsJcmV0dXJuIFRETV9FX09LOw0KPiArfQ0KPiArDQo+ICsvKiBDaGFu
bmVsIExldmVsIEFQSXMgb2YgVERNIEZyYW1ld29yayAqLyBpbnQgdGRtX2NoYW5uZWxfb3Blbih1
MTYgDQo+ICtjaGFuaWQsIHUxNiBjaF93aWR0aCwgc3RydWN0IHRkbV9wb3J0ICpwb3J0LA0KPiAr
CQkJCXZvaWQgKipoX2NoYW5uZWwpDQo+ICt7DQo+ICsJc3RydWN0IHRkbV9jaGFubmVsICpjaGFu
bmVsLCAqdGVtcDsNCj4gKwl1bnNpZ25lZCBsb25nCQlmbGFnczsNCj4gKwlzdHJ1Y3QgdGRtX2No
X2RhdGEJKnBfY2hfZGF0YTsNCj4gKwlpbnQgcmVzID0gVERNX0VfT0s7DQo+ICsNCj4gKwlpZiAo
IShwb3J0ICYmIGhfY2hhbm5lbCkpIHsNCj4gKwkJcHJfZXJyKCIlczogSW52YWxpZCBoYW5kbGVc
biIsIF9fZnVuY19fKTsNCj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ICsJfQ0KPiArDQo+ICsJaWYg
KGNoX3dpZHRoICE9IDEpIHsNCj4gKwkJcHJfZXJyKCIlczogTW9kZSBub3Qgc3VwcG9ydGVkXG4i
LCBfX2Z1bmNfXyk7DQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiArCX0NCj4gKw0KPiArCWxpc3Rf
Zm9yX2VhY2hfZW50cnlfc2FmZShjaGFubmVsLCB0ZW1wLCAmcG9ydC0+bXljaGFubmVscywgbGlz
dCkgew0KPiArCQlpZiAoY2hhbm5lbC0+Y2hfaWQgPT0gY2hhbmlkKSB7DQo+ICsJCQlwcl9lcnIo
IiVzOiBDaGFubmVsICVkIGFscmVhZHkgb3BlblxuIiwNCj4gKwkJCQkJCV9fZnVuY19fLCBjaGFu
aWQpOw0KPiArCQkJcmV0dXJuIC1FSU5WQUw7DQo+ICsJCX0NCj4gKwl9DQo+ICsNCj4gKwljaGFu
bmVsID0ga3phbGxvYyhzaXplb2YoKmNoYW5uZWwpLCBHRlBfS0VSTkVMKTsNCj4gKwlpZiAoIWNo
YW5uZWwpIHsNCj4gKwkJcmVzID0gLUVOT01FTTsNCj4gKwkJZ290byBvdXQ7DQo+ICsJfQ0KPiAr
DQo+ICsJcF9jaF9kYXRhID0ga3phbGxvYyhzaXplb2Yoc3RydWN0IHRkbV9wb3J0X2RhdGEpLCBH
RlBfS0VSTkVMKTsNCj4gKwlpZiAoIXBfY2hfZGF0YSkgew0KPiArCQlyZXMgPSAtRU5PTUVNOw0K
PiArCQlnb3RvIG91dGRhdGE7DQo+ICsJfQ0KPiArDQo+ICsJcF9jaF9kYXRhLT5yeF9kYXRhX2Zp
Zm9bVERNX0NIX1JYX0JEX1JJTkdfU0laRS0xXS53cmFwID0gMTsNCj4gKwlwX2NoX2RhdGEtPnR4
X2RhdGFfZmlmb1tURE1fQ0hfVFhfQkRfUklOR19TSVpFLTFdLndyYXAgPSAxOw0KPiArDQo+ICsJ
cF9jaF9kYXRhLT5yeF9pbl9kYXRhID0gcF9jaF9kYXRhLT5yeF9kYXRhX2ZpZm87DQo+ICsJcF9j
aF9kYXRhLT5yeF9vdXRfZGF0YSA9IHBfY2hfZGF0YS0+cnhfZGF0YV9maWZvOw0KPiArCXBfY2hf
ZGF0YS0+dHhfaW5fZGF0YSA9IHBfY2hfZGF0YS0+dHhfZGF0YV9maWZvOw0KPiArCXBfY2hfZGF0
YS0+dHhfb3V0X2RhdGEgPSBwX2NoX2RhdGEtPnR4X2RhdGFfZmlmbzsNCj4gKwlzcGluX2xvY2tf
aW5pdCgmcF9jaF9kYXRhLT5yeF9jaGFubmVsX2xvY2spOw0KPiArCXNwaW5fbG9ja19pbml0KCZw
X2NoX2RhdGEtPnR4X2NoYW5uZWxfbG9jayk7DQo+ICsNCj4gKwljaGFubmVsLT5wX2NoX2RhdGEg
PSBwX2NoX2RhdGE7DQo+ICsNCj4gKwljaGFubmVsLT5jaF9pZCA9IGNoYW5pZDsNCj4gKwljaGFu
bmVsLT5jaF9jZmcuZmlyc3Rfc2xvdCA9IGNoYW5pZDsNCj4gKwljaGFubmVsLT5jaF9jZmcubnVt
X3Nsb3RzID0gMTsJLyogVGhpcyBpcyAxIGZvciBjaGFubmVsaXplZCBtb2RlIGFuZA0KPiArCQkJ
CQkJY29uZmlndXJhYmxlIGZvciBvdGhlciBtb2RlcyAqLw0KPiArCWNoYW5uZWwtPnBvcnQgPSBw
b3J0Ow0KPiArCWNoYW5uZWwtPmluX3VzZSA9IDE7DQo+ICsNCj4gKwlzcGluX2xvY2tfaXJxc2F2
ZSgmcG9ydC0+Y2hfbGlzdF9sb2NrLCBmbGFncyk7DQo+ICsJbGlzdF9hZGRfdGFpbCgmY2hhbm5l
bC0+bGlzdCwgJnBvcnQtPm15Y2hhbm5lbHMpOw0KPiArCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUo
JnBvcnQtPmNoX2xpc3RfbG9jaywgZmxhZ3MpOw0KPiArDQo+ICsJKmhfY2hhbm5lbCA9IGNoYW5u
ZWw7DQo+ICsNCj4gKwlyZXR1cm4gcmVzOw0KPiArDQo+ICtvdXRkYXRhOg0KPiArCWtmcmVlKGNo
YW5uZWwpOw0KPiArb3V0Og0KPiArCXJldHVybiByZXM7DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9M
KHRkbV9jaGFubmVsX29wZW4pOw0KPiArDQo+ICtpbnQgdGRtX2NoYW5uZWxfY2xvc2UodTE2IGNo
YW5pZCwgdTE2IGNoX3dpZHRoLCBzdHJ1Y3QgdGRtX3BvcnQgKnBvcnQsDQo+ICsJCQkJc3RydWN0
IHRkbV9jaGFubmVsICpoX2NoYW5uZWwpDQo+ICt7DQo+ICsJc3RydWN0IHRkbV9jaGFubmVsICpj
aGFubmVsOw0KPiArCXVuc2lnbmVkIGxvbmcJCWZsYWdzOw0KPiArCWludCByZXMgPSBURE1fRV9P
SzsNCj4gKwljaGFubmVsID0gaF9jaGFubmVsOw0KPiArDQo+ICsJaWYgKCEocG9ydCAmJiBjaGFu
bmVsKSkgew0KPiArCQlwcl9lcnIoIiVzOiBJbnZhbGlkIGhhbmRsZVxuIiwgX19mdW5jX18pOw0K
PiArCQlyZXMgPSAtRUlOVkFMOw0KPiArCQlnb3RvIG91dDsNCj4gKwl9DQo+ICsNCj4gKwlpZiAo
Y2hfd2lkdGggIT0gMSkgew0KPiArCQlwcl9lcnIoIiVzOiBNb2RlIG5vdCBzdXBwb3J0ZWRcbiIs
IF9fZnVuY19fKTsNCj4gKwkJcmVzID0gLUVJTlZBTDsNCj4gKwkJZ290byBvdXQ7DQo+ICsJfQ0K
PiArDQo+ICsJc3Bpbl9sb2NrX2lycXNhdmUoJnBvcnQtPmNoX2xpc3RfbG9jaywgZmxhZ3MpOw0K
PiArCWxpc3RfZGVsKCZjaGFubmVsLT5saXN0KTsNCj4gKwlzcGluX3VubG9ja19pcnFyZXN0b3Jl
KCZwb3J0LT5jaF9saXN0X2xvY2ssIGZsYWdzKTsNCj4gKw0KPiArb3V0Og0KPiArCWlmIChjaGFu
bmVsKQ0KPiArCQlrZnJlZShjaGFubmVsLT5wX2NoX2RhdGEpOw0KPiArCWtmcmVlKGNoYW5uZWwp
Ow0KPiArCXJldHVybiByZXM7DQo+ICt9DQo+ICtFWFBPUlRfU1lNQk9MKHRkbV9jaGFubmVsX2Ns
b3NlKTsNCj4gKw0KPiArdm9pZCBpbml0X2NvbmZpZ19hZGFwdGVyKHN0cnVjdCB0ZG1fYWRhcHRl
ciAqYWRhcCkgew0KPiArCXN0cnVjdCBmc2xfdGRtX2FkYXB0X2NmZyBkZWZhdWx0X2FkYXB0X2Nm
ZyA9IHsNCj4gKwkJLmxvb3BiYWNrID0gZV9URE1fUFJPQ0VTU19OT1JNQUwsDQo+ICsJCS5udW1f
Y2ggPSBOVU1fQ0hBTk5FTFMsDQo+ICsJCS5jaF9zaXplX3R5cGUgPSBDSEFOTkVMXzE2QklUX0xJ
TiwNCj4gKwkJLmZyYW1lX2xlbiA9IE5VTV9TQU1QTEVTX1BFUl9GUkFNRSwNCj4gKwkJLm51bV9m
cmFtZXMgPSBOVU1fU0FNUExFU19QRVJfRlJBTUUsDQo+ICsJCS5hZGFwX21vZGUgPSBlX1RETV9B
REFQVEVSX01PREVfTk9ORQ0KPiArCQkJIH07DQo+ICsNCj4gKwlkZWZhdWx0X2FkYXB0X2NmZy5z
bG90X3dpZHRoID0gZGVmYXVsdF9hZGFwdF9jZmcuY2hfc2l6ZV90eXBlLzMgKyAxOw0KPiArDQo+
ICsJbWVtY3B5KCZhZGFwLT5hZGFwdF9jZmcsICZkZWZhdWx0X2FkYXB0X2NmZywNCj4gKwkJc2l6
ZW9mKHN0cnVjdCBmc2xfdGRtX2FkYXB0X2NmZykpOw0KPiArDQo+ICsJcmV0dXJuOw0KPiArfQ0K
PiArRVhQT1JUX1NZTUJPTChpbml0X2NvbmZpZ19hZGFwdGVyKTsNCj4gKw0KPiArc3RhdGljIHZv
aWQgdGRtX2RhdGFfdGFza2xldF9mbih1bnNpZ25lZCBsb25nIGRhdGEpIHsNCj4gKwlzdHJ1Y3Qg
dGRtX2FkYXB0ZXIgKmFkYXB0ZXI7DQo+ICsJYWRhcHRlciA9IChzdHJ1Y3QgdGRtX2FkYXB0ZXIg
KilkYXRhOw0KPiArCWlmIChhZGFwdGVyICE9IE5VTEwpIHsNCj4gKwkJdGRtX2RhdGFfdHhfaW50
ZXJsZWF2ZShhZGFwdGVyKTsNCj4gKwkJdGRtX2RhdGFfcnhfZGVpbnRlcmxlYXZlKGFkYXB0ZXIp
Ow0KPiArCX0NCj4gK30NCj4gKw0KPiArDQo+ICtNT0RVTEVfQVVUSE9SKCJIZW1hbnQgQWdyYXdh
bCA8aGVtYW50QGZyZWVzY2FsZS5jb20+IGFuZCAiDQo+ICsJIlJhamVzaCBHdW1hc3RhIDxyYWpl
c2guZ3VtYXN0YUBmcmVlc2NhbGUuY29tPiIpOyANCj4gK01PRFVMRV9ERVNDUklQVElPTigiVERN
IERyaXZlciBGcmFtZXdvcmsgQ29yZSIpOyANCj4gK01PRFVMRV9MSUNFTlNFKCJHUEwiKTsNCj4g
ZGlmZiAtLWdpdCBhL2luY2x1ZGUvbGludXgvbW9kX2RldmljZXRhYmxlLmggDQo+IGIvaW5jbHVk
ZS9saW51eC9tb2RfZGV2aWNldGFibGUuaCBpbmRleCBhZTI4ZTkzLi5kYzFhNjU1IDEwMDY0NA0K
PiAtLS0gYS9pbmNsdWRlL2xpbnV4L21vZF9kZXZpY2V0YWJsZS5oDQo+ICsrKyBiL2luY2x1ZGUv
bGludXgvbW9kX2RldmljZXRhYmxlLmgNCj4gQEAgLTQxNiw2ICs0MTYsMTcgQEAgc3RydWN0IGky
Y19kZXZpY2VfaWQgew0KPiAgCQkJX19hdHRyaWJ1dGVfXygoYWxpZ25lZChzaXplb2Yoa2VybmVs
X3Vsb25nX3QpKSkpOw0KPiAgfTsNCj4gIA0KPiArLyogdGRtICovDQo+ICsNCj4gKyNkZWZpbmUg
VERNX05BTUVfU0laRSAgIDIwDQo+ICsjZGVmaW5lIFRETV9NT0RVTEVfUFJFRklYICJ0ZG06Ig0K
PiArDQo+ICtzdHJ1Y3QgdGRtX2RldmljZV9pZCB7DQo+ICsJY2hhciBuYW1lW1RETV9OQU1FX1NJ
WkVdOw0KPiArCWtlcm5lbF91bG9uZ190IGRyaXZlcl9kYXRhICAgICAgLyogRGF0YSBwcml2YXRl
IHRvIHRoZSBkcml2ZXIgKi8NCj4gKwkJCV9fYXR0cmlidXRlX18oKGFsaWduZWQoc2l6ZW9mKGtl
cm5lbF91bG9uZ190KSkpKTsNCj4gK307DQo+ICsNCj4gIC8qIHNwaSAqLw0KPiAgDQo+ICAjZGVm
aW5lIFNQSV9OQU1FX1NJWkUJMzINCj4gZGlmZiAtLWdpdCBhL2luY2x1ZGUvbGludXgvdGRtLmgg
Yi9pbmNsdWRlL2xpbnV4L3RkbS5oIG5ldyBmaWxlIG1vZGUgDQo+IDEwMDY0NCBpbmRleCAwMDAw
MDAwLi44Y2Y0ZWY1DQo+IC0tLSAvZGV2L251bGwNCj4gKysrIGIvaW5jbHVkZS9saW51eC90ZG0u
aA0KPiBAQCAtMCwwICsxLDM0NyBAQA0KPiArLyogaW5jbHVkZS9saW51eC90ZG0uaA0KPiArICoN
Cj4gKyAqIENvcHlyaWdodCAoQykgMjAxMiBGcmVlc2NhbGUgU2VtaWNvbmR1Y3RvciwgSW5jLCBB
bGwgcmlnaHRzIHJlc2VydmVkLg0KPiArICoNCj4gKyAqIHRkbS5oIC0gZGVmaW5pdGlvbnMgZm9y
IHRoZSB0ZG0tZGV2aWNlIGZyYW1ld29yayBpbnRlcmZhY2UNCj4gKyAqDQo+ICsgKiBBdXRob3I6
SGVtYW50IEFncmF3YWwgPGhlbWFudEBmcmVlc2NhbGUuY29tPg0KPiArICoJUmFqZXNoIEd1bWFz
dGEgPHJhamVzaC5ndW1hc3RhQGZyZWVzY2FsZS5jb20+DQo+ICsgKg0KPiArICogVGhpcyBwcm9n
cmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlICBpdCBhbmQvb3IgDQo+
ICttb2RpZnkgaXQNCj4gKyAqIHVuZGVyICB0aGUgdGVybXMgb2YgIHRoZSBHTlUgR2VuZXJhbCAg
UHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIA0KPiArYnkgdGhlDQo+ICsgKiBGcmVlIFNvZnR3
YXJlIEZvdW5kYXRpb247ICBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSAgTGljZW5zZSwgb3IgDQo+
ICsoYXQgeW91cg0KPiArICogb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4NCj4gKyAqDQo+ICsg
KiBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJl
IHVzZWZ1bCwgDQo+ICtidXQNCj4gKyAqIFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2
ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YNCj4gKyAqIE1FUkNIQU5UQUJJTElUWSBvciBGSVRO
RVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUgR05VDQo+ICsgKiBHZW5lcmFs
IFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuDQo+ICsgKg0KPiArICogWW91IHNob3Vs
ZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl
IA0KPiArYWxvbmcNCj4gKyAqIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlICB0byB0
aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCANCj4gK0luYy4sDQo+ICsgKiA2NzUgTWFzcyBB
dmUsIENhbWJyaWRnZSwgTUEgMDIxMzksIFVTQS4NCj4gKyAqLw0KPiArDQo+ICsNCj4gKyNpZm5k
ZWYgX0xJTlVYX1RETV9IDQo+ICsjZGVmaW5lIF9MSU5VWF9URE1fSA0KPiArDQo+ICsjaWZkZWYg
X19LRVJORUxfXw0KPiArI2luY2x1ZGUgPGxpbnV4L3R5cGVzLmg+DQo+ICsjaW5jbHVkZSA8bGlu
dXgvbW9kdWxlLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvbW9kX2RldmljZXRhYmxlLmg+DQo+ICsj
aW5jbHVkZSA8bGludXgvZGV2aWNlLmg+CS8qIGZvciBzdHJ1Y3QgZGV2aWNlICovDQo+ICsjaW5j
bHVkZSA8bGludXgvc2NoZWQuaD4JLyogZm9yIGNvbXBsZXRpb24gKi8NCj4gKyNpbmNsdWRlIDxs
aW51eC9tdXRleC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPg0KPiArDQo+ICsj
ZGVmaW5lIENIQU5ORUxfOEJJVF9MSU4JMAkvKiA4IGJpdCBsaW5lYXIgKi8NCj4gKyNkZWZpbmUg
Q0hBTk5FTF84QklUX1VMQVcJMQkvKiA4IGJpdCBNdS1sYXcgKi8NCj4gKyNkZWZpbmUgQ0hBTk5F
TF84QklUX0FMQVcJMgkvKiA4IGJpdCBBLWxhdyAqLw0KPiArI2RlZmluZSBDSEFOTkVMXzE2QklU
X0xJTgkzCS8qIDE2IGJpdCBMaW5lYXIgKi8NCj4gKw0KPiArI2RlZmluZSBOVU1fQ0hBTk5FTFMJ
CTE2DQo+ICsjZGVmaW5lIE5VTV9TQU1QTEVTX1BFUl9NUwk4CQkvKiA4IHNhbXBsZXMgcGVyIG1p
bGxpIHNlYyBwZXINCj4gKwkJCQkJCSBjaGFubmVsLiBSZXEgZm9yIHZvaWNlIGRhdGEgKi8NCj4g
KyNkZWZpbmUgTlVNX01TCQkJMTANCj4gKyNkZWZpbmUgTlVNX1NBTVBMRVNfUEVSX0ZSQU1FCShO
VU1fTVMgKiBOVU1fU0FNUExFU19QRVJfTVMpIC8qIE51bWJlciBvZg0KPiArCQkJCQkJc2FtcGxl
cyBmb3IgMSBjbGllbnQgYnVmZmVyICovDQo+ICsjZGVmaW5lIE5VTV9PRl9URE1fQlVGCQkzDQo+
ICsNCj4gKy8qIEdlbmVyYWwgb3B0aW9ucyAqLw0KPiArDQo+ICtzdHJ1Y3QgdGRtX2FkYXB0X2Fs
Z29yaXRobTsNCj4gK3N0cnVjdCB0ZG1fYWRhcHRlcjsNCj4gK3N0cnVjdCB0ZG1fcG9ydDsNCj4g
K3N0cnVjdCB0ZG1fZHJpdmVyOw0KPiArDQo+ICsvKiBBbGlnbiBhZGRyIG9uIGEgc2l6ZSBib3Vu
ZGFyeSAtIGFkanVzdCBhZGRyZXNzIHVwIGlmIG5lZWRlZCAqLw0KPiArLyogcmV0dXJucyBtaW4g
dmFsdWUgZ3JlYXRlciB0aGFuIHNpemUgd2hpY2ggaXMgbXVsdGlwbGUgb2YgYWxpZ25tZW50IA0K
PiArKi8gc3RhdGljIGlubGluZSBpbnQgQUxJR05fU0laRSh1NjQgc2l6ZSwgdTMyIGFsaWdubWVu
dCkgew0KPiArCXJldHVybiAoc2l6ZSArIGFsaWdubWVudCAtIDEpICYgKH4oYWxpZ25tZW50IC0g
MSkpOyB9DQo+ICsNCj4gKy8qKg0KPiArICogc3RydWN0IHRkbV9kcml2ZXIgLSByZXByZXNlbnQg
YW4gVERNIGRldmljZSBkcml2ZXINCj4gKyAqIEBjbGFzczogV2hhdCBraW5kIG9mIHRkbSBkZXZp
Y2Ugd2UgaW5zdGFudGlhdGUgKGZvciBkZXRlY3QpDQo+ICsgKiBAaWQ6RHJpdmVyIGlkDQo+ICsg
KiBAbmFtZTogTmFtZSBvZiB0aGUgZHJpdmVyDQo+ICsgKiBAYXR0YWNoX2FkYXB0ZXI6IENhbGxi
YWNrIGZvciBkZXZpY2UgYWRkaXRpb24gKGZvciBsZWdhY3kgZHJpdmVycykNCj4gKyAqIEBkZXRh
Y2hfYWRhcHRlcjogQ2FsbGJhY2sgZm9yIGRldmljZSByZW1vdmFsIChmb3IgbGVnYWN5IGRyaXZl
cnMpDQo+ICsgKiBAcHJvYmU6IENhbGxiYWNrIGZvciBkZXZpY2UgYmluZGluZw0KPiArICogQHJl
bW92ZTogQ2FsbGJhY2sgZm9yIGRldmljZSB1bmJpbmRpbmcNCj4gKyAqIEBzaHV0ZG93bjogQ2Fs
bGJhY2sgZm9yIGRldmljZSBzaHV0ZG93bg0KPiArICogQHN1c3BlbmQ6IENhbGxiYWNrIGZvciBk
ZXZpY2Ugc3VzcGVuZA0KPiArICogQHJlc3VtZTogQ2FsbGJhY2sgZm9yIGRldmljZSByZXN1bWUN
Cj4gKyAqIEBjb21tYW5kOiBDYWxsYmFjayBmb3Igc2VuZGluZyBjb21tYW5kcyB0byBkZXZpY2UN
Cj4gKyAqIEBpZF90YWJsZTogTGlzdCBvZiBURE0gZGV2aWNlcyBzdXBwb3J0ZWQgYnkgdGhpcyBk
cml2ZXINCj4gKyAqIEBsaXN0OiBMaXN0IG9mIGRyaXZlcnMgY3JlYXRlZCAoZm9yIHRkbS1jb3Jl
IHVzZSBvbmx5KSAgKi8gc3RydWN0IA0KPiArdGRtX2RyaXZlciB7DQo+ICsJdW5zaWduZWQgaW50
IGNsYXNzOw0KPiArCXVuc2lnbmVkIGludCBpZDsNCj4gKwljaGFyIG5hbWVbVERNX05BTUVfU0la
RV07DQo+ICsNCj4gKwlpbnQgKCphdHRhY2hfYWRhcHRlcikoc3RydWN0IHRkbV9hZGFwdGVyICop
Ow0KPiArCWludCAoKmRldGFjaF9hZGFwdGVyKShzdHJ1Y3QgdGRtX2FkYXB0ZXIgKik7DQo+ICsN
Cj4gKwkvKiBTdGFuZGFyZCBkcml2ZXIgbW9kZWwgaW50ZXJmYWNlcyAqLw0KPiArCWludCAoKnBy
b2JlKShjb25zdCBzdHJ1Y3QgdGRtX2RldmljZV9pZCAqKTsNCj4gKwlpbnQgKCpyZW1vdmUpKHZv
aWQpOw0KPiArDQo+ICsJLyogZHJpdmVyIG1vZGVsIGludGVyZmFjZXMgdGhhdCBkb24ndCByZWxh
dGUgdG8gZW51bWVyYXRpb24gKi8NCj4gKwl2b2lkICgqc2h1dGRvd24pKHZvaWQpOw0KPiArCWlu
dCAoKnN1c3BlbmQpKHBtX21lc3NhZ2VfdCBtZXNnKTsNCj4gKwlpbnQgKCpyZXN1bWUpKHZvaWQp
Ow0KPiArDQo+ICsJLyogYSBpb2N0bCBsaWtlIGNvbW1hbmQgdGhhdCBjYW4gYmUgdXNlZCB0byBw
ZXJmb3JtIHNwZWNpZmljIGZ1bmN0aW9ucw0KPiArCSAqIHdpdGggdGhlIGRldmljZS4NCj4gKwkg
Ki8NCj4gKwlpbnQgKCpjb21tYW5kKSh1bnNpZ25lZCBpbnQgY21kLCB2b2lkICphcmcpOw0KPiAr
DQo+ICsJY29uc3Qgc3RydWN0IHRkbV9kZXZpY2VfaWQgKmlkX3RhYmxlOw0KPiArDQo+ICsJLyog
VGhlIGFzc29jaWF0ZWQgYWRhcHRlciBmb3IgdGhpcyBkcml2ZXIgKi8NCj4gKwlzdHJ1Y3QgdGRt
X2FkYXB0ZXIgKmFkYXB0ZXI7DQo+ICsJc3RydWN0IGxpc3RfaGVhZCBsaXN0Ow0KPiArfTsNCj4g
Kw0KPiArLyogdGRtIHBlciBwb3J0IHN0YXRpc3RpY3Mgc3RydWN0dXJlLCB1c2VkIGZvciBwcm92
aWRpbmcgYW5kIHN0b3JpbmcgDQo+ICt0ZG0gcG9ydA0KPiArICogc3RhdGlzdGljcy4NCj4gKyAq
Lw0KPiArc3RydWN0IHRkbV9wb3J0X3N0YXRzIHsNCj4gKwl1bnNpZ25lZCBpbnQgcnhfcGt0X2Nv
dW50OwkvKiBSeCBmcmFtZSBjb3VudCBwZXIgY2hhbm5lbCAqLw0KPiArCXVuc2lnbmVkIGludCBy
eF9wa3RfZHJvcF9jb3VudDsJLyogUnggZHJvcCBjb3VudCBwZXIgY2hhbm5lbCB0bw0KPiArCQkJ
CQkgY2xlYW4gc3BhY2UgZm9yIG5ldyBidWZmZXIgKi8NCj4gKwl1bnNpZ25lZCBpbnQgdHhfcGt0
X2NvdW50OwkvKiBUeCBmcmFtZSBjb3VudCBwZXIgY2hhbm5lbCAqLw0KPiArCXVuc2lnbmVkIGlu
dCB0eF9wa3RfY29uZl9jb3VudDsJLyogVHggZnJhbWUgY29uZmlybWF0aW9uIGNvdW50IHBlcg0K
PiArCQkJCQkgY2hhbm5lbCAqLw0KPiArCXVuc2lnbmVkIGludCB0eF9wa3RfZHJvcF9jb3VudDsJ
LyogVHggZHJvcCBjb3VudCBwZXIgY2hhbm5lbCBkdWUgdG8NCj4gKwkJCQkJIHF1ZXVlIGZ1bGwg
Ki8NCj4gK307DQo+ICsNCj4gKw0KPiArLyogdGRtIEJ1ZmZlciBEZXNjcmlwdG9yLCB1c2VkIGZv
ciBDcmVhdGluZyBJbnRlcmxlYXZlZCBhbmQgDQo+ICtEZS1pbnRlcmxlYXZlZA0KPiArICogRklG
T3MNCj4gKyAqLw0KPiArc3RydWN0IHRkbV9iZCB7DQo+ICsJdW5zaWduZWQgY2hhciBmbGFnOwkJ
LyogQkQgaXMgZnVsbCBvciBlbXB0eSAqLw0KPiArCXVuc2lnbmVkIGNoYXIgd3JhcDsJCS8qIEJE
IGlzIGxhc3QgaW4gdGhlIHF1ZXVlICovDQo+ICsJdW5zaWduZWQgc2hvcnQgbGVuZ3RoOwkvKiBM
ZW5ndGggb2YgRGF0YSBpbiBCRCAqLw0KPiArCS8qVE9ETzogdXNlIGR5YW5taWMgbWVtb3J5ICov
DQo+ICsJdW5zaWduZWQgc2hvcnQgcF9kYXRhW05VTV9TQU1QTEVTX1BFUl9GUkFNRV07CS8qIERh
dGEgUG9pbnRlciAqLw0KPiArCXVuc2lnbmVkIGxvbmcgb2Zmc2V0OwkvKiBPZmZzZXQgb2YgdGhl
IERhdGEgUG9pbnRlciB0byBiZSB1c2VkICovDQo+ICt9Ow0KPiArDQo+ICsjZGVmaW5lIFRETV9D
SF9SWF9CRF9SSU5HX1NJWkUJMw0KPiArI2RlZmluZSBURE1fQ0hfVFhfQkRfUklOR19TSVpFCTMN
Cj4gKw0KPiArLyogdGRtIFJYLVRYIENoYW5uZWxpc2VkIERhdGEgKi8NCj4gK3N0cnVjdCB0ZG1f
cG9ydF9kYXRhIHsNCj4gKwlzdHJ1Y3QgdGRtX2JkIHJ4X2RhdGFfZmlmb1tURE1fQ0hfUlhfQkRf
UklOR19TSVpFXTsgLyogUnggQ2hhbm5lbCBEYXRhDQo+ICsJCQkJCQkJCUJEIFJpbmcgKi8NCj4g
KwlzdHJ1Y3QgdGRtX2JkICpyeF9pbl9kYXRhOwkvKiBDdXJyZW50IENoYW5uZWwgUnggQkQgdG8g
YmUgZmlsbGVkIGJ5DQo+ICsJCQkJCQlkZS1pbnRlcmxlYXZlIGZ1bmN0aW9uICovDQo+ICsJc3Ry
dWN0IHRkbV9iZCAqcnhfb3V0X2RhdGE7CS8qIEN1cnJlbnQgQ2hhbm5lbCBSeCBCRCB0byBiZQ0K
PiArCQkJCQkJCXJlYWQgYnkgQXBwICovDQo+ICsJc3RydWN0IHRkbV9iZCB0eF9kYXRhX2ZpZm9b
VERNX0NIX1RYX0JEX1JJTkdfU0laRV07IC8qIFR4IENoYW5uZWwgRGF0YQ0KPiArCQkJCQkJCQlC
RCBSaW5nICovDQo+ICsJc3RydWN0IHRkbV9iZCAqdHhfaW5fZGF0YTsJLyogQ3VycmVudCBDaGFu
bmVsIFR4IEJEIHRvIGJlDQo+ICsJCQkJCQkgZmlsbGVkIGJ5IEFwcCAqLw0KPiArCXN0cnVjdCB0
ZG1fYmQgKnR4X291dF9kYXRhOwkvKiBDdXJyZW50IENoYW5uZWwgVHggQkQgdG8gYmUgcmVhZCBi
eQ0KPiArCQkJCQkJaW50ZXJsZWF2ZSBmdW5jdGlvbiAqLw0KPiArCXNwaW5sb2NrX3QgcnhfY2hh
bm5lbF9sb2NrOwkvKiBTcGluIExvY2sgZm9yIFJ4IENoYW5uZWwgKi8NCj4gKwlzcGlubG9ja190
IHR4X2NoYW5uZWxfbG9jazsJLyogU3BpbiBMb2NrIGZvciBUeCBDaGFubmVsICovDQo+ICt9Ow0K
PiArDQo+ICsvKiBzdHJ1Y3R1cmUgdGRtX3BvcnRfY2ZnIC0gY29udGFpbnMgY29uZmlndXJhdGlv
biBwYXJhbXMgZm9yIGEgcG9ydCANCj4gKyovIHN0cnVjdCB0ZG1fcG9ydF9jZmcgew0KPiArCXVu
c2lnbmVkIHNob3J0IHBvcnRfbW9kZTsNCj4gK307DQo+ICsNCj4gKy8qIHN0cnVjdCB0ZG1fcG9y
dCAtIHJlcHJlc2VudCBhbiBURE0gcG9ydHMgZm9yIGEgZGV2aWNlICovIHN0cnVjdCANCj4gK3Rk
bV9wb3J0IHsNCj4gKwl1bnNpZ25lZCBzaG9ydCBwb3J0X2lkOw0KPiArCXVuc2lnbmVkIHNob3J0
IGluX3VzZTsJCS8qIFBvcnQgaXMgZW5hYmxlZD8gKi8NCj4gKwl1aW50MTZfdCByeF9tYXhfZnJh
bWVzOwkJLyogUmVjZWl2ZWQgUG9ydCBmcmFtZXMNCj4gKwkJCQkJIGJlZm9yZSBhbGxvd2luZyBS
ZWFkIE9wZXJhdGlvbiBpbg0KPiArCQkJCQkgUG9ydCBNb2RlICovDQo+ICsNCj4gKwlzdHJ1Y3Qg
dGRtX3BvcnRfc3RhdHMgcG9ydF9zdGF0Oy8qIEEgc3RydWN0dXJlIHBhcmFtZXRlcnMgZGVmaW5p
bmcNCj4gKwkJCQkJIFRETSBwb3J0IHN0YXRpc3RpY3MuICovDQo+ICsJc3RydWN0IHRkbV9wb3J0
X2RhdGEgKnBfcG9ydF9kYXRhOwkvKiBhIHN0cnVjdHVyZSBwYXJhbWV0ZXJzDQo+ICsJCQkJCQlk
ZWZpbmluZyB0ZG0gY2hhbm5lbGlzZWQgZGF0YSAqLw0KPiArCXdhaXRfcXVldWVfaGVhZF90IGNo
X3dhaXRfcXVldWU7CS8qIHdhaXRRdWV1ZSBmb3IgUlggUG9ydCBEYXRhICovDQo+ICsNCj4gKwlz
dHJ1Y3QgdGRtX2RyaXZlciAqZHJpdmVyOwkvKiBkcml2ZXIgZm9yIHRoaXMgcG9ydCAqLw0KPiAr
CXN0cnVjdCB0ZG1fYWRhcHRlciAqYWRhcHRlcjsJLyogYWRhcHRlciBmb3IgdGhpcyBwb3J0ICov
DQo+ICsJc3RydWN0IGxpc3RfaGVhZCBsaXN0OwkJLyogbGlzdCBvZiBwb3J0cyAqLw0KPiArCXN0
cnVjdCBsaXN0X2hlYWQgbXljaGFubmVsczsJLyogbGlzdCBvZiBjaGFubmVscywgY3JlYXRlZCBv
biB0aGlzDQo+ICsJCQkJCSBwb3J0Ki8NCj4gKwlzcGlubG9ja190IGNoX2xpc3RfbG9jazsJLyog
U3BpbiBMb2NrIGZvciBjaGFubmVsX2xpc3QgKi8NCj4gKwlzdHJ1Y3QgdGRtX3BvcnRfY2ZnIHBv
cnRfY2ZnOy8qIEEgc3RydWN0dXJlIHBhcmFtZXRlcnMgZGVmaW5pbmcNCj4gKwkJCQkJIFRETSBw
b3J0IGNvbmZpZ3VyYXRpb24uICovDQo+ICt9Ow0KPiArDQo+ICsvKiB0ZG0gUlgtVFggQ2hhbm5l
bGlzZWQgRGF0YSAqLw0KPiArc3RydWN0IHRkbV9jaF9kYXRhIHsNCj4gKwlzdHJ1Y3QgdGRtX2Jk
IHJ4X2RhdGFfZmlmb1tURE1fQ0hfUlhfQkRfUklOR19TSVpFXTsgLyogUnggUG9ydCBEYXRhIEJE
DQo+ICsJCQkJCQkJCVJpbmcgKi8NCj4gKwlzdHJ1Y3QgdGRtX2JkICpyeF9pbl9kYXRhOwkvKiBD
dXJyZW50IFBvcnQgUnggQkQgdG8gYmUgZmlsbGVkIGJ5DQo+ICsJCQkJCQlkZS1pbnRlcmxlYXZl
IGZ1bmN0aW9uICovDQo+ICsJc3RydWN0IHRkbV9iZCAqcnhfb3V0X2RhdGE7IC8qIEN1cnJlbnQg
UG9ydCBSeCBCRCB0byBiZSByZWFkIGJ5IEFwcCAqLw0KPiArCXN0cnVjdCB0ZG1fYmQgdHhfZGF0
YV9maWZvW1RETV9DSF9UWF9CRF9SSU5HX1NJWkVdOyAvKiBUeCBQb3J0IERhdGEgQkQNCj4gKwkJ
CQkJCQkJUmluZyAqLw0KPiArCXN0cnVjdCB0ZG1fYmQgKnR4X2luX2RhdGE7CS8qIEN1cnJlbnQg
UG9ydCBUeCBCRCB0byBiZSBmaWxsZWQgYnkNCj4gKwkJCQkJCUFwcCAqLw0KPiArCXN0cnVjdCB0
ZG1fYmQgKnR4X291dF9kYXRhOwkvKiBDdXJyZW50IFBvcnQgVHggQkQgdG8gYmUgcmVhZCBieQ0K
PiArCQkJCQkJaW50ZXJsZWF2ZSBmdW5jdGlvbiAqLw0KPiArCXNwaW5sb2NrX3QgcnhfY2hhbm5l
bF9sb2NrOwkvKiBTcGluIExvY2sgZm9yIFJ4IFBvcnQgKi8NCj4gKwlzcGlubG9ja190IHR4X2No
YW5uZWxfbG9jazsJLyogU3BpbiBMb2NrIGZvciBUeCBQb3J0ICovDQo+ICt9Ow0KPiArDQo+ICsv
KiBDaGFubmVsIGNvbmZpZyBwYXJhbXMgKi8NCj4gK3N0cnVjdCB0ZG1fY2hfY2ZnIHsNCj4gKwl1
bnNpZ25lZCBzaG9ydCBudW1fc2xvdHM7DQo+ICsJdW5zaWduZWQgc2hvcnQgZmlyc3Rfc2xvdDsN
Cj4gK307DQo+ICsNCj4gKy8qIHN0cnVjdCB0ZG1fY2hhbm5lbC0gcmVwcmVzZW50IGEgVERNIGNo
YW5uZWwgZm9yIGEgcG9ydCAqLyBzdHJ1Y3QgDQo+ICt0ZG1fY2hhbm5lbCB7DQo+ICsJdTE2IGNo
X2lkOwkJCS8qIGxvZ2ljYWwgY2hhbm5lbCBudW1iZXIgKi8NCj4gKwlzdHJ1Y3QgbGlzdF9oZWFk
IGxpc3Q7CQkvKiBsaXN0IG9mIGNoYW5uZWxzIGluIGEgcG9ydCovDQo+ICsJc3RydWN0IHRkbV9w
b3J0ICpwb3J0OwkJLyogcG9ydCBmb3IgdGhpcyBjaGFubmVsICovDQo+ICsJdTE2IGluX3VzZTsJ
CQkvKiBjaGFubmVsIGlzIGVuYWJsZWQ/ICovDQo+ICsJc3RydWN0IHRkbV9jaF9jZmcgY2hfY2Zn
OwkvKiBjaGFubmVsIGNvbmZpZ3VyYXRpb24gKi8NCj4gKwlzdHJ1Y3QgdGRtX2NoX2RhdGEgKnBf
Y2hfZGF0YTsJLyogZGF0YSBzdG9yYWdlIHNwYWNlIGZvciBjaGFubmVsICovDQo+ICt9Ow0KPiAr
DQo+ICsvKiB0ZG1fYWRhcHRfYWxnb3JpdGhtIGlzIGZvciBhY2Nlc3NpbmcgdGhlIHJvdXRpbmVz
IG9mIGRldmljZSAqLyANCj4gK3N0cnVjdCB0ZG1fYWRhcHRfYWxnb3JpdGhtIHsNCj4gKwl1MzIg
KCp0ZG1fcmVhZCkoc3RydWN0IHRkbV9hZGFwdGVyICosIHUxNiAqKik7DQo+ICsJdTMyICgqdGRt
X2dldF93cml0ZV9idWYpKHN0cnVjdCB0ZG1fYWRhcHRlciAqLCB1MTYgKiopOw0KPiArCXUzMiAo
KnRkbV93cml0ZSkoc3RydWN0IHRkbV9hZGFwdGVyICosIHZvaWQgKiAsIHVuc2lnbmVkIGludCBs
ZW4pOw0KPiArCWludCAoKnRkbV9lbmFibGUpKHN0cnVjdCB0ZG1fYWRhcHRlciAqKTsNCj4gKwlp
bnQgKCp0ZG1fZGlzYWJsZSkoc3RydWN0IHRkbV9hZGFwdGVyICopOyB9Ow0KPiArDQo+ICsvKiB0
ZG1fYWRhcHRlcl9tb2RlIGlzIHRvIGRlZmluZSBpbiBtb2RlIG9mIHRoZSBkZXZpY2UgKi8gZW51
bSANCj4gK3RkbV9hZGFwdGVyX21vZGUgew0KPiArCWVfVERNX0FEQVBURVJfTU9ERV9OT05FID0g
MHgwMCwNCj4gKwllX1RETV9BREFQVEVSX01PREVfVDEgPSAweDAxLA0KPiArCWVfVERNX0FEQVBU
RVJfTU9ERV9FMSA9IDB4MDIsDQo+ICsJZV9URE1fQURBUFRFUl9NT0RFX1QxX1JBVyA9IDB4MTAs
DQo+ICsJZV9URE1fQURBUFRFUl9NT0RFX0UxX1JBVyA9IDB4MjAsDQo+ICt9Ow0KPiArDQo+ICsv
KiB0ZG1fcG9ydF9tb2RlIGRlZmluZXMgdGhlIG1vZGUgaW4gd2hpY2ggdGhlIHBvcnQgaXMgY29u
ZmlndXJlZCB0byANCj4gK29wZXJhdGUNCj4gKyAqIEl0IGNhbiBiZSBjaGFubmVsaXplZC9mdWxs
L2ZyYWN0aW9uYWwuDQo+ICsgKi8NCj4gK2VudW0gdGRtX3BvcnRfbW9kZSB7DQo+ICsJZV9URE1f
UE9SVF9DSEFOTkVMSVpFRCA9IDAJLyogQ2hhbm5lbGl6ZWQgbW9kZSAqLw0KPiArCSwgZV9URE1f
UE9SVF9GVUxMID0gMQkJLyogRnVsbCBtb2RlICovDQo+ICsJLCBlX1RETV9QT1JUX0ZSQUNUSU9O
QUwgPSAyCS8qIEZyYWN0aW9uYWwgbW9kZSAqLw0KPiArfTsNCj4gKw0KPiArLyogdGRtX3Byb2Nl
c3NfbW9kZSB1c2VkIGZvciB0ZXN0aW5nIHRoZSB0ZG0gZGV2aWNlIGluIG5vcm1hbCBtb2RlIG9y
IA0KPiAraW50ZXJuYWwNCj4gKyAqIGxvb3BiYWNrIG9yIGV4dGVybmFsIGxvb3BiYWNrDQo+ICsg
Ki8NCj4gK2VudW0gdGRtX3Byb2Nlc3NfbW9kZSB7DQo+ICsJZV9URE1fUFJPQ0VTU19OT1JNQUwg
PSAwCS8qIE5vcm1hbCBtb2RlICovDQo+ICsJLCBlX1RETV9QUk9DRVNTX0lOVF9MUEIgPSAxCS8q
IEludGVybmFsIGxvb3AgbW9kZSAqLw0KPiArCSwgZV9URE1fUFJPQ0VTU19FWFRfTFBCID0gMgkv
KiBFeHRlcm5hbCBMb29wYmFjayBtb2RlICovDQo+ICt9Ow0KPiArDQo+ICsNCj4gKy8qIFRETSBj
b25maWd1cmF0aW9uIHBhcmFtZXRlcnMgKi8NCj4gK3N0cnVjdCBmc2xfdGRtX2FkYXB0X2NmZyB7
DQo+ICsJdTggbnVtX2NoOwkJLyogTnVtYmVyIG9mIGNoYW5uZWxzIGluIHRoaXMgYWRwYXRlciAq
Lw0KPiArCXU4IGNoX3NpemVfdHlwZTsJCS8qIHJlY2lldmVyL3RyYW5zbWl0IGNoYW5uZWwNCj4g
KwkJCQkJCXNpemUgZm9yIGFsbCBjaGFubmVscyAqLw0KPiArCXU4IHNsb3Rfd2lkdGg7CQkvKiAx
IG9yIDIgSXMgZGVmaW5lZCBieSBjaGFubmVsIHR5cGUgKi8NCj4gKwl1OCBmcmFtZV9sZW47CQkv
KiBMZW5ndGggb2YgZnJhbWUgaW4gc2FtcGxlcyAqLw0KPiArCXUzMiBudW1fZnJhbWVzOw0KPiAr
CXU4IGxvb3BiYWNrOwkJCS8qIGxvb3BiYWNrIG9yIG5vcm1hbCAqLw0KPiArCXU4IGFkYXBfbW9k
ZTsJCQkvKiAwPU5vbmUsIDE9IFQxLCAyPSBUMS1GVUxMLCAzPUUxLA0KPiArCQkJCQkJNCA9IEUx
LUZVTEwgKi8NCj4gKwlpbnQgbWF4X251bV9wb3J0czsJCS8qIE5vdCBVc2VkOiBNYXggTnVtYmVy
IG9mIHBvcnRzIHRoYXQNCj4gKwkJCQkJY2FuIGJlIGNyZWF0ZWQgb24gdGhpcyBhZGFwdGVyICov
DQo+ICsJaW50IG1heF90aW1lc2xvdHM7CQkvKiBNYXggTnVtYmVyIG9mIHRpbWVzbG90cyB0aGF0
IGFyZQ0KPiArCQkJCQlzdXBwb3J0ZWQgb24gdGhpcyBhZGFwdGVyICovDQo+ICt9Ow0KPiArDQo+
ICsvKg0KPiArICogdGRtX2FkYXB0ZXIgaXMgdGhlIHN0cnVjdHVyZSB1c2VkIHRvIGlkZW50aWZ5
IGEgcGh5c2ljYWwgdGRtIA0KPiArZGV2aWNlIGFsb25nDQo+ICsgKiB3aXRoIHRoZSBhY2Nlc3Mg
YWxnb3JpdGhtcyBuZWNlc3NhcnkgdG8gYWNjZXNzIGl0Lg0KPiArICovDQo+ICtzdHJ1Y3QgdGRt
X2FkYXB0ZXIgew0KPiArCXN0cnVjdCBtb2R1bGUgKm93bmVyOwkvKiBvd25lciBvZiB0aGUgYWRh
cHRlciBtb2R1bGUgKi8NCj4gKwl1bnNpZ25lZCBpbnQgaWQ7CS8qIEFkYXB0ZXIgSWQgKi8NCj4g
Kwl1bnNpZ25lZCBpbnQgY2xhc3M7CS8qIGNsYXNzZXMgdG8gYWxsb3cgcHJvYmluZyBmb3IgKi8N
Cj4gKwl1bnNpZ25lZCBpbnQgZHJ2X2NvdW50OwkvKiBOdW1iZXIgb2YgZHJpdmVycyBhc3NvY2lh
dGVkIHdpdGggdGhlDQo+ICsJCQkJIGFkYXB0ZXIgKi8NCj4gKw0KPiArCWNvbnN0IHN0cnVjdCB0
ZG1fYWRhcHRfYWxnb3JpdGhtICphbGdvOwkvKiB0aGUgYWxnb3JpdGhtIHRvIGFjY2VzcyB0aGUN
Cj4gKwkJCQkJCSBhZGFwdGVyKi8NCj4gKw0KPiArCWNoYXIgbmFtZVtURE1fTkFNRV9TSVpFXTsJ
LyogTmFtZSBvZiBBZGFwdGVyICovDQo+ICsJc3RydWN0IG11dGV4IGFkYXBfbG9jazsNCj4gKwlz
dHJ1Y3QgZGV2aWNlICpwYXJlbnQ7CQkvKk5vdCBVc2VkKi8NCj4gKw0KPiArCXN0cnVjdCB0YXNr
bGV0X3N0cnVjdCB0ZG1fZGF0YV90YXNrbGV0OwkvKiB0YXNrbGV0IGhhbmRsZSB0byBwZXJmb3Jt
DQo+ICsJCQkJCQkgZGF0YSBwcm9jZXNzaW5nKi8NCj4gKwlpbnQgdGFza2xldF9jb25mOwkvKiBm
bGFnIGZvciB0YXNrbGV0IGNvbmZpZ3VyYXRpb24gKi8NCj4gKwlpbnQgdGRtX3J4X2ZsYWc7DQo+
ICsNCj4gKwlzdHJ1Y3QgbGlzdF9oZWFkIG15cG9ydHM7CS8qIGxpc3Qgb2YgcG9ydHMsIGNyZWF0
ZWQgb24gdGhpcw0KPiArCQkJCQkgYWRhcHRlciAqLw0KPiArCXN0cnVjdCBsaXN0X2hlYWQgbGlz
dDsNCj4gKwlzcGlubG9ja190IHBvcnRsaXN0X2xvY2s7CS8qIFNwaW4gTG9jayBmb3IgcG9ydF9s
aXN0ICovDQo+ICsJdm9pZCAqZGF0YTsNCj4gKwlzdHJ1Y3QgZnNsX3RkbV9hZGFwdF9jZmcgYWRh
cHRfY2ZnOw0KPiArfTsNCj4gKw0KPiArc3RhdGljIGlubGluZSB2b2lkICp0ZG1fZ2V0X2FkYXBk
YXRhKGNvbnN0IHN0cnVjdCB0ZG1fYWRhcHRlciAqZGV2KSB7DQo+ICsJcmV0dXJuIGRldi0+ZGF0
YTsNCj4gK30NCj4gKw0KPiArc3RhdGljIGlubGluZSB2b2lkIHRkbV9zZXRfYWRhcGRhdGEoc3Ry
dWN0IHRkbV9hZGFwdGVyICpkZXYsIHZvaWQgDQo+ICsqZGF0YSkgew0KPiArCWRldi0+ZGF0YSA9
IGRhdGE7DQo+ICt9DQo+ICsNCj4gKy8qIGZ1bmN0aW9ucyBleHBvcnRlZCBieSB0ZG0ubyAqLw0K
PiArDQo+ICtleHRlcm4gaW50IHRkbV9hZGRfYWRhcHRlcihzdHJ1Y3QgdGRtX2FkYXB0ZXIgKik7
IGV4dGVybiBpbnQgDQo+ICt0ZG1fZGVsX2FkYXB0ZXIoc3RydWN0IHRkbV9hZGFwdGVyICopOyBl
eHRlcm4gaW50IA0KPiArdGRtX3JlZ2lzdGVyX2RyaXZlcihzdHJ1Y3QgdGRtX2RyaXZlciAqKTsg
ZXh0ZXJuIHZvaWQgDQo+ICt0ZG1fZGVsX2RyaXZlcihzdHJ1Y3QgdGRtX2RyaXZlciAqKTsgZXh0
ZXJuIHZvaWQgDQo+ICt0ZG1fdW5yZWdpc3Rlcl9kcml2ZXIoc3RydWN0IHRkbV9kcml2ZXIgKik7
IGV4dGVybiB2b2lkIA0KPiAraW5pdF9jb25maWdfYWRhcHRlcihzdHJ1Y3QgdGRtX2FkYXB0ZXIg
Kik7DQo+ICsNCj4gK2V4dGVybiB1bnNpZ25lZCBpbnQgdGRtX3BvcnRfb3BlbihzdHJ1Y3QgdGRt
X2RyaXZlciAqLCB2b2lkICoqKTsgDQo+ICtleHRlcm4gdW5zaWduZWQgaW50IHRkbV9wb3J0X2Ns
b3NlKHZvaWQgKik7IGV4dGVybiB1bnNpZ25lZCBpbnQgDQo+ICt0ZG1fcG9ydF9pb2N0bCh2b2lk
ICosIHVuc2lnbmVkIGludCwgdW5zaWduZWQgbG9uZyk7IGV4dGVybiB1bnNpZ25lZCANCj4gK2lu
dCB0ZG1fY2hhbm5lbF9yZWFkKHZvaWQgKiwgdm9pZCAqLCB2b2lkICosIHUxNiAqKTsgZXh0ZXJu
IHVuc2lnbmVkIA0KPiAraW50IHRkbV9jaGFubmVsX3dyaXRlKHZvaWQgKiwgdm9pZCAqICwgdm9p
ZCAqLCB1MTYpOyBleHRlcm4gdW5zaWduZWQgDQo+ICtpbnQgdGRtX3BvcnRfcG9sbCh2b2lkICos
IHVuc2lnbmVkIGludCk7DQo+ICsNCj4gK2V4dGVybiBpbnQgdGRtX2NoYW5uZWxfb3Blbih1MTYs
IHUxNiwgc3RydWN0IHRkbV9wb3J0ICosIHZvaWQgKiopOyANCj4gK2V4dGVybiBpbnQgdGRtX2No
YW5uZWxfY2xvc2UodTE2LCB1MTYsIHN0cnVjdCB0ZG1fcG9ydCAqLA0KPiArCQkJCQkJc3RydWN0
IHRkbV9jaGFubmVsICopOw0KPiArDQo+ICtzdGF0aWMgaW5saW5lIGludCB0ZG1fYWRkX2RyaXZl
cihzdHJ1Y3QgdGRtX2RyaXZlciAqZHJpdmVyKSB7DQo+ICsJcmV0dXJuIHRkbV9yZWdpc3Rlcl9k
cml2ZXIoZHJpdmVyKTsNCj4gK30NCj4gKw0KPiArZXh0ZXJuIHN0cnVjdCB0ZG1fYWRhcHRlciAq
dGRtX2dldF9hZGFwdGVyKGludCBpZCk7IGV4dGVybiB2b2lkIA0KPiArdGRtX3B1dF9hZGFwdGVy
KHN0cnVjdCB0ZG1fYWRhcHRlciAqYWRhcCk7DQo+ICsNCj4gKyNlbmRpZiAvKiBfX0tFUk5FTF9f
ICovDQo+ICsNCj4gKyNkZWZpbmUgVERNX0VfT0sgMA0KPiArDQo+ICsjZW5kaWYgLyogX0xJTlVY
X1RETV9IICovDQoNCg0KDQo=

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH][2/3][RFC] TDM Framework
  2012-04-24  0:34 ` Scott Wood
@ 2012-04-27  2:07   ` Aggrwal Poonam-B10812
  0 siblings, 0 replies; 6+ messages in thread
From: Aggrwal Poonam-B10812 @ 2012-04-27  2:07 UTC (permalink / raw)
  To: Wood Scott-B07421; +Cc: linuxppc-dev, Singh Sandeep-B37400


> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Tuesday, April 24, 2012 6:05 AM
> To: Aggrwal Poonam-B10812
> Cc: linuxppc-dev@lists.ozlabs.org; Singh Sandeep-B37400
> Subject: Re: [PATCH][2/3][RFC] TDM Framework
>=20
Thanks Scott for the comments, we will incorporate them and send the next v=
ersion.

Regards
Poonam
> On 03/10/2012 06:57 AM, Poonam Aggrwal wrote:
> > diff --git a/drivers/Kconfig b/drivers/Kconfig index ad6c1eb..25f7f5b
> > 100644
> > --- a/drivers/Kconfig
> > +++ b/drivers/Kconfig
> > @@ -130,4 +130,5 @@ source "drivers/virt/Kconfig"
> >
> >  source "drivers/net/dpa/NetCommSw/Kconfig"
> >
> > +source "drivers/tdm/Kconfig"
> >  endmenu
>=20
> When posting patches to this list, please ensure that they are based on
> an upstream Linux kernel and not a Freescale BSP kernel.
Sure, but this was RFC patch set where the primary intention was to get des=
ign level comments.
But we will send the updated patches rebased on upstream head.
>=20
> > +if TDM
> > +
> > +config TDM_DEBUG_CORE
> > +	bool "TDM Core debugging messages"
> > +	help
> > +	  Say Y here if you want the TDM core to produce a bunch of debug
> > +	  messages to the system log.  Select this if you are having a
> > +	  problem with TDM support and want to see more of what is going
> on.
> > +
> > +endif # TDM
>=20
> Please use the normal kernel mechanisms for controlling debug messages.
>=20
okay
> > diff --git a/drivers/tdm/tdm-core.c b/drivers/tdm/tdm-core.c new file
> > mode 100644 index 0000000..cdda260
> > --- /dev/null
> > +++ b/drivers/tdm/tdm-core.c
> > @@ -0,0 +1,1146 @@
> > +/* driver/tdm/tdm-core.c
> > + *
> > + * Copyright (C) 2012 Freescale Semiconductor, Inc, All rights
> reserved.
> > + *
> > + * TDM core is the interface between TDM clients and TDM devices.
> > + * It is also intended to serve as an interface for line controld
> > + * devices later on.
> > + *
> > + * Author:Hemant Agrawal <hemant@freescale.com>
> > + *	Rajesh Gumasta <rajesh.gumasta@freescale.com>
> > + *
> > + * Modified by Sandeep Kr Singh <sandeep@freescale.com>
> > + *		Poonam Aggarwal <poonam.aggarwal@freescale.com>
> > + * 1. Added framework based initialization of device.
> > + * 2. All the init/run time configuration is now done by framework.
> > + * 3. Added channel level operations.
> > + *
> > + * This program 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 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.
> > + *
> > + * 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.,
> > + * 675 Mass Ave, Cambridge, MA 02139, USA.
> > + */
> > +
> > +/* if read write debug required */
> > +#undef TDM_CORE_DEBUG
> > +
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/slab.h>
> > +#include <linux/tdm.h>
> > +#include <linux/init.h>
> > +#include <linux/idr.h>
> > +#include <linux/mutex.h>
> > +#include <linux/completion.h>
> > +#include <linux/hardirq.h>
> > +#include <linux/irqflags.h>
> > +#include <linux/list.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/io.h>
> > +#include "device/tdm_fsl.h"
>=20
> What is a reference to tdm_fsl.h doing in the presumably hardware-
> independent tdm-core.c?
Need to check that, ideally this include should not be required
>=20
> > +static DEFINE_MUTEX(tdm_core_lock);
> > +static DEFINE_IDR(tdm_adapter_idr);
> > +/* List of TDM adapters registered with TDM framework */
> > +LIST_HEAD(adapter_list);
> > +
> > +/* List of TDM clients registered with TDM framework */
> > +LIST_HEAD(driver_list);
> > +
> > +/* In case the previous data is not fetched by the client driver, the
> > + * de-interleaving function will  discard the old data and rewrite
> > +the
> > + * new data */
> > +static int use_latest_tdm_data =3D 1;
>=20
> Describe when one would want to change this, and provide a better way to
> change it (e.g. module parameter or sysfs knob).
>=20
Ok, description can be added.
Will also explore the sysfs knob option.                                   =
                                                                           =
                                                                           =
 =20
> /*
>  * Linux kernel
>  * multi-line comment style
>  * is like this.
>  */
>=20
Sure
> > +/* this tasklet is created for each adapter instance */ static void
> > +tdm_data_tasklet_fn(unsigned long);
> > +
> > +/* tries to match client driver with the adapter */ static int
> > +tdm_device_match(struct tdm_driver *driver, struct tdm_adapter *adap)
> > +{
> > +	/* match on an id table if there is one */
> > +	if (driver->id_table && driver->id_table->name[0]) {
> > +		if (!(strcmp(driver->id_table->name, adap->name)))
> > +			return (int)driver->id_table;
> > +	}
> > +	return TDM_E_OK;
> > +}
>=20
> s/TDM_E_OK/0/g
>=20
ok
> > +static int tdm_attach_driver_adap(struct tdm_driver *driver,
> > +					struct tdm_adapter *adap)
> > +{
> > +	/* if driver is already attached to any other adapter, return*/
> > +	if (driver->adapter && (driver->adapter !=3D adap))
> > +		return TDM_E_OK;
>=20
> Why can't one driver service multiple adapters?  How would multiple
> drivers service one adapter?  Are there sub-devices that need individual
> controlling?
>=20
Driver is here client driver which would be using=20
> > +	driver->adapter =3D adap;
> > +
> > +	if (driver->attach_adapter) {
> > +		if (driver->attach_adapter(adap) < 0)
> > +			/* We ignore the return code; if it fails, too bad */
> > +			pr_err("attach_adapter failed for driver [%s]\n",
> > +				driver->name);
>=20
> Why ignore errors?
This will be fixed.

>=20
> > +/* Detach client driver and adapter */ static int
> > +tdm_detach_driver_adap(struct tdm_driver *driver,
> > +					struct tdm_adapter *adap)
> > +{
> > +	int res =3D TDM_E_OK;
> > +
> > +	if (!driver->adapter || (driver->adapter !=3D adap))
> > +		return TDM_E_OK;
>=20
> Shouldn't this be an error?
Will check this.

>=20
> > +	if (!driver->detach_adapter)
> > +		return TDM_E_OK;
> > +
> > +	adap->drv_count--;
>=20
> If the driver doesn't have a detach_adapter method, you skip decrementing
> the count and leave the tasklet lying around?
>=20
This has been fixed in later version of the patch which we made for FSL BSP=
.
Will fix this here also.
> > +/* TDM adapter Registration/De-registration with TDM framework */
> > +
> > +static int tdm_register_adapter(struct tdm_adapter *adap) {
> > +	int res =3D TDM_E_OK;
> > +	struct tdm_driver *driver, *next;
> > +
> > +	if (!adap) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return -EINVAL;
> > +	}
>=20
> Pointers aren't handles.  The caller should not be passing NULL, and it
> would be more useful to crash and get a backtrace if it does.  It's not
> realistic to check every pointer for being NULL when there's no
> legitimate reason it could be NULL, and it doesn't help you if you have
> some other bad value besides NULL.
Okay, we will check this.
>=20
> > +	mutex_init(&adap->adap_lock);
> > +	INIT_LIST_HEAD(&adap->myports);
> > +	spin_lock_init(&adap->portlist_lock);
> > +
> > +	adap->drv_count =3D 0;
> > +	adap->tasklet_conf =3D 0;
> > +
> > +	list_add_tail(&adap->list, &adapter_list);
> > +
> > +	/* initialization of driver by framework in default configuration
> */
> > +	init_config_adapter(adap);
> > +
> > +	/* Notify drivers */
> > +	pr_info("adapter [%s] registered\n", adap->name);
>=20
> This is too noisy.  You haven't even gotten a match yet.
>=20
It is telling that these adapters have registered to the framework.
> > +/*
> > + * tdm_add_adapter - declare tdm adapter, use dynamic device number
>=20
> Why are there device numbers at all?
>=20
> I suspect there's a fair bit of copy and paste going on of another
> subsystem's quirks (i2c?).  I don't see any mention in the copyright
> block of this code having been derived from anything else, though...
>=20
When we initially started, we took i2c as an example. But later not much co=
uld be used as-is.
But yes some initial stuff is from i2c. Will update it in copyright section=
.
> > +
> > +
> > +/**
> > + * tdm_del_adapter - unregister TDM adapter
> > + * @adap: the adapter being unregistered
> > + *
> > + * This unregisters an TDM adapter which was previously registered
> > + * by @tdm_add_adapter.
> > + */
> > +int tdm_del_adapter(struct tdm_adapter *adap) {
> > +	int res =3D TDM_E_OK;
> > +	struct tdm_adapter *found;
> > +	struct tdm_driver *driver, *next;
> > +
> > +	if (!adap) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* First make sure that this adapter was ever added */
> > +	mutex_lock(&tdm_core_lock);
> > +	found =3D idr_find(&tdm_adapter_idr, adap->id);
> > +	mutex_unlock(&tdm_core_lock);
> > +	if (found !=3D adap) {
> > +		pr_err("tdm-core: attempting to delete unregistered "
> > +			 "adapter [%s]\n", adap->name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*disable and kill the data processing tasklet */
> > +	if (adap->tasklet_conf) {
> > +		tasklet_disable(&adap->tdm_data_tasklet);
> > +		tasklet_kill(&adap->tdm_data_tasklet);
> > +		adap->tasklet_conf =3D 0;
> > +	}
> > +
> > +	/* Detach any active ports. This can't fail, thus we do not
> > +	   checking the returned value. */
> > +	mutex_lock(&tdm_core_lock);
> > +	list_for_each_entry_safe(driver, next, &driver_list, list) {
> > +		if (tdm_device_match(driver, adap)) {
> > +			tdm_detach_driver_adap(driver, adap);
> > +			pr_info(
> > +			"Driver(ID=3D%d) is detached from Adapter %s(ID =3D %d)\n",
> > +				 driver->id, adap->name, adap->id);
> > +		}
> > +	}
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	mutex_lock(&tdm_core_lock);
> > +	idr_remove(&tdm_adapter_idr, adap->id);
> > +	mutex_unlock(&tdm_core_lock);
>=20
> Why are you dropping the lock then reacquiring it?
>=20
Mistake, will fix it.
> > +/* TDM Client Drivers Registration/De-registration Functions */ int
> > +tdm_register_driver(struct tdm_driver *driver) {
> > +	int res =3D TDM_E_OK;
> > +	struct tdm_adapter *adap, *next;
> > +
> > +	list_add_tail(&driver->list, &driver_list);
> > +
> > +	mutex_lock(&tdm_core_lock);
> > +	/* Walk the adapters that are already present */
> > +	list_for_each_entry_safe(adap, next, &adapter_list, list) {
> > +		if (tdm_device_match(driver, adap)) {
> > +			res =3D tdm_attach_driver_adap(driver, adap);
> > +			pr_info("TDM Driver(ID=3D%d)is attached with Adapter"
> > +				"%s(ID =3D %d) drv_count=3D%d", driver->id,
> > +				adap->name, adap->id, adap->drv_count);
> > +		break;
> > +		}
> > +	}
>=20
> Indentation.
>=20
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	return res;
> > +}
> > +EXPORT_SYMBOL(tdm_register_driver);
> > +
> > +/*
> > + * tdm_unregister_driver - unregister TDM client driver from TDM
> > +framework
> > + * @driver: the driver being unregistered  */ void
> > +tdm_unregister_driver(struct tdm_driver *driver) {
> > +	if (!driver) {
> > +		pr_err("%s:Invalid handle\n", __func__);
> > +		return;
> > +	}
> > +       /* A driver can register to only one adapter,
> > +	* so no need to browse the list */
>=20
> Whitespace.
>=20
> > +	mutex_lock(&tdm_core_lock);
> > +	tdm_detach_driver_adap(driver, driver->adapter);
> > +	mutex_unlock(&tdm_core_lock);
> > +
> > +	list_del(&driver->list);
> > +
> > +	pr_debug("tdm-core: driver [%s] unregistered\n", driver->name); }
> > +EXPORT_SYMBOL(tdm_unregister_driver);
> > +
> > +/* TDM Framework init and exit */
> > +static int __init tdm_init(void)
> > +{
> > +	pr_info("%s\n", __func__);
> > +	return TDM_E_OK;
> > +}
> > +
> > +static void __exit tdm_exit(void)
> > +{
> > +	pr_info("%s\n", __func__);
> > +	return;
> > +}
>=20
> Don't spam the console just because the driver got loaded or unloaded (at
> this point you haven't even found the hardware).
>=20
Ok

> > +/* We must initialize early, because some subsystems register tdm
> > +drivers
> > + * in subsys_initcall() code, but are linked (and initialized) before
> tdm.
> > + */
> > +postcore_initcall(tdm_init);
> > +module_exit(tdm_exit);
>=20
> tdm_init doesn't do anything, so why does it need to run early?
>=20
> > +/* Port Level APIs of TDM Framework */ unsigned int
> > +tdm_port_open(struct tdm_driver *driver, void **h_port)
>=20
> Why is the return unsigned int?  You're returning negative numbers.
>=20
> Also consider having the return be a pointer, and use PTR_ERR/ERR_PTR --
> or at least put a proper type on h_port (what is the "h_"?).
>=20
> > +{
> > +	struct tdm_port *port;
> > +	struct tdm_adapter *adap;
> > +	unsigned long		flags;
> > +	int res =3D TDM_E_OK;
> > +
> > +	if (driver =3D=3D NULL) {
> > +		pr_err("driver NULL\n");
> > +		return -ENODEV;
> > +	}
> > +	if (driver->adapter =3D=3D NULL) {
> > +		pr_err("adapter NULL\n");
> > +		return -ENODEV;
> > +	}
>=20
> Either make these pr_debug (or remove them), or make the message more
> specific.
>=20
> > +	adap =3D tdm_get_adapter(driver->adapter->id);
> > +	if (!adap)
> > +		return -ENODEV;
> > +
> > +	/* This creates an anonymous tdm_port, which may later be
> > +	 * pointed to some slot.
> > +	 *
> > +	 */
> > +	port =3D kzalloc(sizeof(*port), GFP_KERNEL);
> > +	if (!port) {
> > +		res =3D -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	init_waitqueue_head(&port->ch_wait_queue);
> > +
> > +
> > +	port->rx_max_frames =3D NUM_SAMPLES_PER_FRAME;
> > +	port->port_cfg.port_mode =3D e_TDM_PORT_CHANNELIZED;
> > +
> > +	port->in_use =3D 1;
>=20
> When would a port have in_use be false, other than this brief window
> where nothing else should be looking at it?
>=20
> > +	list_for_each_entry_safe(channel, temp, &port->mychannels, list) {
> > +	if (channel)
> > +		if (channel->in_use) {
> > +			pr_err("%s: Cannot close port. Channel in use\n",
> > +								__func__);
> > +			res =3D -ENXIO;
> > +			goto out;
> > +			}
> > +	}
>=20
> Broken indentation.
>=20
> > +unsigned int tdm_channel_read(void *h_port, void *h_channel,
> > +				void *p_data, u16 *size)
> > +{
> > +	struct tdm_port *port;
> > +	struct tdm_channel *channel;
> > +	struct tdm_bd *rx_bd;
> > +	unsigned long flags;
> > +	int i, res =3D TDM_E_OK;
> > +	unsigned short *buf, *buf1;
> > +	port =3D (struct tdm_port *)h_port;
> > +	channel =3D (struct tdm_channel *)h_channel;
>=20
> Unnecessary casts.
>=20
> > +	if ((port && channel) =3D=3D 0) { /* invalid handle*/
>=20
> This is an odd construct -- how about "if (!port || !channel)"?
>=20
> > +		pr_err("%s:Invalid Handle\n", __func__);
> > +		return -ENXIO;
> > +	}
> > +
> > +	if (!port->in_use)
> > +		return -EIO;
> > +	if (!channel->p_ch_data || !channel->in_use)
> > +		return -EIO;
> > +
> > +	spin_lock_irqsave(&channel->p_ch_data->rx_channel_lock, flags);
>=20
> Shouldn't you test whether it's in use after you get the lock?
>=20
> > +	rx_bd =3D channel->p_ch_data->rx_out_data;
> > +
> > +	if (rx_bd->flag) {
> > +		*size =3D rx_bd->length;
> > +		buf =3D (u16 *) p_data;
> > +		buf1 =3D (u16 *)rx_bd->p_data;
> > +		for (i =3D 0; i < NUM_SAMPLES_PER_FRAME; i++)
> > +			buf[i] =3D buf1[i];
> > +		rx_bd->flag =3D 0;
> > +		rx_bd->offset =3D 0;
> > +		channel->p_ch_data->rx_out_data =3D (rx_bd->wrap) ?
> > +				channel->p_ch_data->rx_data_fifo : rx_bd + 1;
> > +
> > +	} else {
> > +		spin_unlock_irqrestore(&channel->p_ch_data->rx_channel_lock,
> > +						flags);
> > +		pr_info("No Data Available");
> > +		return -EAGAIN;
> > +	}
>=20
> That pr_info() is inappropriate.  This driver appears to be overly chatty
> in general (and with quite vague messages) -- or is this debug stuff that
> will be removed in a non-RFC patch?
>=20
> > +unsigned int tdm_port_get_stats(void *h_port, struct tdm_port_stats
> > +*portStat) {
> > +	struct tdm_port *port;
> > +	int port_num;
> > +	port =3D (struct tdm_port *)h_port;
> > +
> > +	if (port =3D=3D NULL || portStat =3D=3D NULL) { /* invalid handle*/
> > +		pr_err("Invalid Handle");
> > +		return -ENXIO;
> > +	}
> > +	port_num =3D  port->port_id;
> > +
> > +	memcpy(portStat, &port->port_stat, sizeof(struct tdm_port_stats));
> > +
> > +	pr_info("TDM Port %d Get Stats", port_num);
> > +
> > +	return TDM_E_OK;
> > +}
> > +EXPORT_SYMBOL(tdm_port_get_stats);
> > +
> > +/* Data handling functions */
> > +
> > +static int tdm_data_rx_deinterleave(struct tdm_adapter *adap) {
> > +	struct tdm_port *port, *next;
> > +	struct tdm_channel *channel, *temp;
> > +	struct tdm_bd	*ch_bd;
> > +
> > +	int i, buf_size, ch_data_len;
> > +	u16 *input_tdm_buffer;
> > +	u16 *pcm_buffer;
> > +	int slot_width;
> > +	int frame_ch_data_size;
> > +	bool ch_data;
> > +	int bytes_in_fifo_per_frame;
> > +	int bytes_slot_offset;
> > +
> > +	ch_data_len =3D NUM_SAMPLES_PER_FRAME;
> > +	frame_ch_data_size =3D NUM_SAMPLES_PER_FRAME;
> > +	ch_data =3D 0;
> > +
> > +	if (!adap) { /* invalid handle*/
> > +		pr_err("%s: Invalid Handle\n", __func__);
> > +		return -ENXIO;
> > +	}
> > +
> > +	slot_width =3D adap->adapt_cfg.slot_width;
> > +	buf_size =3D tdm_adap_recv(adap, (void **)&input_tdm_buffer);
> > +	if (buf_size <=3D 0 || !input_tdm_buffer)
> > +		return -EINVAL;
> > +
> > +	bytes_in_fifo_per_frame =3D buf_size/frame_ch_data_size;
> > +	bytes_slot_offset =3D bytes_in_fifo_per_frame/slot_width;
> > +
> > +	/* de-interleaving for all ports*/
> > +	list_for_each_entry_safe(port, next, &adap->myports, list) {
> > +
> > +		/* if the port is not open */
> > +		if (!port->in_use)
> > +			continue;
> > +
> > +		list_for_each_entry_safe(channel, temp, &port->mychannels,
> > +							list) {
> > +		/* if the channel is not open */
> > +		if (!channel->in_use || !channel->p_ch_data)
> > +			continue;
> > +		ch_bd =3D channel->p_ch_data->rx_in_data;
> > +		spin_lock(&channel->p_ch_data->rx_channel_lock);
> > +			/*if old data is to be discarded */
> > +		if (use_latest_tdm_data)
> > +			if (ch_bd->flag) {
> > +				ch_bd->flag =3D 0;
> > +				ch_bd->offset =3D 0;
> > +				if (ch_bd =3D=3D channel->p_ch_data->rx_out_data)
> > +					channel->p_ch_data->rx_out_data =3D
> > +						ch_bd->wrap ?
> > +						channel->p_ch_data->rx_data_fifo
> > +						: ch_bd+1;
> > +					port->port_stat.rx_pkt_drop_count++;
> > +				}
> > +			/* if the bd is empty */
> > +			if (!ch_bd->flag) {
> > +				if (ch_bd->offset =3D=3D 0)
> > +					ch_bd->length =3D port->rx_max_frames;
> > +
> > +				pcm_buffer =3D ch_bd->p_data + ch_bd->offset;
> > +				/* De-interleaving the data */
> > +				for (i =3D 0; i < ch_data_len; i++) {
> > +					pcm_buffer[i]
> > +					=3D input_tdm_buffer[i*bytes_slot_offset +
> > +						channel->ch_id];
> > +				}
> > +				ch_bd->offset +=3D ch_data_len * slot_width;
> > +
> > +				if (ch_bd->offset >=3D
> > +					(ch_bd->length - frame_ch_data_size)*
> > +						(adap->adapt_cfg.slot_width)) {
> > +					ch_bd->flag =3D 1;
> > +					ch_bd->offset =3D 0;
> > +					channel->p_ch_data->rx_in_data =3D
> > +						ch_bd->wrap ?
> > +						channel->p_ch_data->rx_data_fifo
> > +						: ch_bd+1;
> > +					ch_data =3D 1;
> > +				}
> > +			} else {
> > +				port->port_stat.rx_pkt_drop_count++;
> > +			}
> > +		spin_unlock(&channel->p_ch_data->rx_channel_lock);
> > +		}
>=20
> Broken indentation.  Spaces around operators.
>=20
> > +int tdm_channel_close(u16 chanid, u16 ch_width, struct tdm_port *port,
> > +				struct tdm_channel *h_channel)
> > +{
> > +	struct tdm_channel *channel;
> > +	unsigned long		flags;
> > +	int res =3D TDM_E_OK;
> > +	channel =3D h_channel;
> > +
> > +	if (!(port && channel)) {
> > +		pr_err("%s: Invalid handle\n", __func__);
> > +		res =3D -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	if (ch_width !=3D 1) {
> > +		pr_err("%s: Mode not supported\n", __func__);
> > +		res =3D -EINVAL;
> > +		goto out;
> > +	}
>=20
> close() seems an odd time to be checking for supported channel width,
> especially since you don't use that variable anywhere else in this
> function.
>=20
> > +#ifndef _LINUX_TDM_H
> > +#define _LINUX_TDM_H
> > +
> > +#ifdef __KERNEL__
>=20
> Is this supposed to be a userspace header ever?  If TDM exposes a
> userspace interface, it needs to be documented.
>=20
> > +#include <linux/types.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/device.h>	/* for struct device */
> > +#include <linux/sched.h>	/* for completion */
> > +#include <linux/mutex.h>
> > +#include <linux/interrupt.h>
> > +
> > +#define CHANNEL_8BIT_LIN	0	/* 8 bit linear */
> > +#define CHANNEL_8BIT_ULAW	1	/* 8 bit Mu-law */
> > +#define CHANNEL_8BIT_ALAW	2	/* 8 bit A-law */
> > +#define CHANNEL_16BIT_LIN	3	/* 16 bit Linear */
> > +
> > +#define NUM_CHANNELS		16
> > +#define NUM_SAMPLES_PER_MS	8		/* 8 samples per milli sec per
> > +						 channel. Req for voice data */
> > +#define NUM_MS			10
> > +#define NUM_SAMPLES_PER_FRAME	(NUM_MS * NUM_SAMPLES_PER_MS) /*
> Number of
> > +						samples for 1 client buffer */
> > +#define NUM_OF_TDM_BUF		3
>=20
> These need proper namespacing -- plus, should all of these really be
> hardcoded like this?  Is this standard stuff that will always be the
> same?
>=20
> > +/* General options */
> > +
> > +struct tdm_adapt_algorithm;
> > +struct tdm_adapter;
> > +struct tdm_port;
> > +struct tdm_driver;
> > +
> > +/* Align addr on a size boundary - adjust address up if needed */
> > +/* returns min value greater than size which is multiple of alignment
> > +*/ static inline int ALIGN_SIZE(u64 size, u32 alignment) {
> > +	return (size + alignment - 1) & (~(alignment - 1)); }
>=20
> Use the already existing ALIGN().
>=20
> > +/**
> > + * struct tdm_driver - represent an TDM device driver
> > + * @class: What kind of tdm device we instantiate (for detect)
> > + * @id:Driver id
> > + * @name: Name of the driver
> > + * @attach_adapter: Callback for device addition (for legacy drivers)
> > + * @detach_adapter: Callback for device removal (for legacy drivers)
> > + * @probe: Callback for device binding
> > + * @remove: Callback for device unbinding
> > + * @shutdown: Callback for device shutdown
> > + * @suspend: Callback for device suspend
> > + * @resume: Callback for device resume
> > + * @command: Callback for sending commands to device
> > + * @id_table: List of TDM devices supported by this driver
> > + * @list: List of drivers created (for tdm-core use only)  */ struct
> > +tdm_driver {
> > +	unsigned int class;
> > +	unsigned int id;
> > +	char name[TDM_NAME_SIZE];
> > +
> > +	int (*attach_adapter)(struct tdm_adapter *);
> > +	int (*detach_adapter)(struct tdm_adapter *);
> > +
> > +	/* Standard driver model interfaces */
> > +	int (*probe)(const struct tdm_device_id *);
> > +	int (*remove)(void);
> > +
> > +	/* driver model interfaces that don't relate to enumeration */
> > +	void (*shutdown)(void);
> > +	int (*suspend)(pm_message_t mesg);
> > +	int (*resume)(void);
> > +
> > +	/* a ioctl like command that can be used to perform specific
> functions
> > +	 * with the device.
> > +	 */
> > +	int (*command)(unsigned int cmd, void *arg);
>=20
> Please describe what semantics you expect for the non-standard functions.
>=20
> Where are the "ioctl like commands" defined?  When would they be used?
> I don't see it used in this patchset.
>=20
> > +/* struct tdm_channel- represent a TDM channel for a port */ struct
> > +tdm_channel {
> > +	u16 ch_id;			/* logical channel number */
> > +	struct list_head list;		/* list of channels in a port*/
> > +	struct tdm_port *port;		/* port for this channel */
> > +	u16 in_use;			/* channel is enabled? */
> > +	struct tdm_ch_cfg ch_cfg;	/* channel configuration */
> > +	struct tdm_ch_data *p_ch_data;	/* data storage space for
> channel */
> > +};
>=20
> Why are ch_id and especially in_use u16?
>=20
> > +/* tdm_adapt_algorithm is for accessing the routines of device */
> > +struct tdm_adapt_algorithm {
> > +	u32 (*tdm_read)(struct tdm_adapter *, u16 **);
> > +	u32 (*tdm_get_write_buf)(struct tdm_adapter *, u16 **);
> > +	u32 (*tdm_write)(struct tdm_adapter *, void * , unsigned int len);
> > +	int (*tdm_enable)(struct tdm_adapter *);
> > +	int (*tdm_disable)(struct tdm_adapter *); };
> >
>=20
> Provide parameter names and document the semantics you're expecting.
>=20
> > +/* tdm_adapter_mode is to define in mode of the device */ enum
> > +tdm_adapter_mode {
> > +	e_TDM_ADAPTER_MODE_NONE =3D 0x00,
> > +	e_TDM_ADAPTER_MODE_T1 =3D 0x01,
> > +	e_TDM_ADAPTER_MODE_E1 =3D 0x02,
> > +	e_TDM_ADAPTER_MODE_T1_RAW =3D 0x10,
> > +	e_TDM_ADAPTER_MODE_E1_RAW =3D 0x20,
> > +};
> > +
> > +/* tdm_port_mode defines the mode in which the port is configured to
> > +operate
> > + * It can be channelized/full/fractional.
> > + */
> > +enum tdm_port_mode {
> > +	e_TDM_PORT_CHANNELIZED =3D 0	/* Channelized mode */
> > +	, e_TDM_PORT_FULL =3D 1		/* Full mode */
> > +	, e_TDM_PORT_FRACTIONAL =3D 2	/* Fractional mode */
> > +};
> > +
> > +/* tdm_process_mode used for testing the tdm device in normal mode or
> > +internal
> > + * loopback or external loopback
> > + */
> > +enum tdm_process_mode {
> > +	e_TDM_PROCESS_NORMAL =3D 0	/* Normal mode */
> > +	, e_TDM_PROCESS_INT_LPB =3D 1	/* Internal loop mode */
> > +	, e_TDM_PROCESS_EXT_LPB =3D 2	/* External Loopback mode */
> > +};
>=20
> Commas go at the end of lines, not the beginning.
>=20
> No hungarian notation -- drop the "e_".
>=20
> > +/* TDM configuration parameters */
> > +struct fsl_tdm_adapt_cfg {
> > +	u8 num_ch;		/* Number of channels in this adpater */
> > +	u8 ch_size_type;		/* reciever/transmit channel
> > +						size for all channels */
> > +	u8 slot_width;		/* 1 or 2 Is defined by channel type */
> > +	u8 frame_len;		/* Length of frame in samples */
>=20
> Is u8 really appropriate here?  Maybe int?
>=20
> What does "type" mean in "ch_size_type"?
>=20
> > +	u8 adap_mode;			/* 0=3DNone, 1=3D T1, 2=3D T1-FULL, 3=3DE1,
> > +						4 =3D E1-FULL */
>=20
> Use #defines or an enum.
>=20
> > +	int max_num_ports;		/* Not Used: Max Number of ports that
> > +					can be created on this adapter */
>=20
> If it's not used, why is it here?
>=20
> > +/*
> > + * tdm_adapter is the structure used to identify a physical tdm
> > +device along
> > + * with the access algorithms necessary to access it.
> > + */
> > +struct tdm_adapter {
> > +	struct module *owner;	/* owner of the adapter module */
> > +	unsigned int id;	/* Adapter Id */
>=20
> What does the id mean?  Why do we need an arbitrary numberspace?
>=20
> > +	unsigned int class;	/* classes to allow probing for */
>=20
> What sort of values would go here?
>=20
> > +	unsigned int drv_count;	/* Number of drivers associated with the
> > +				 adapter */
> > +
> > +	const struct tdm_adapt_algorithm *algo;	/* the algorithm to
> access the
> > +						 adapter*/
> > +
> > +	char name[TDM_NAME_SIZE];	/* Name of Adapter */
> > +	struct mutex adap_lock;
> > +	struct device *parent;		/*Not Used*/
>=20
> Why is the parent device not used?
>=20
> > +	struct tasklet_struct tdm_data_tasklet;	/* tasklet handle to
> perform
> > +						 data processing*/
> > +	int tasklet_conf;	/* flag for tasklet configuration */
> > +	int tdm_rx_flag;
>=20
> What does "tdm_rx_flag" indicate?  What about "tasklet_conf"?
>=20
> > +	struct list_head myports;	/* list of ports, created on this
> > +					 adapter */
> > +	struct list_head list;
>=20
> If you've got more than one list, it's probably a bad idea for any of
> them to be simply called "list".
>=20
> > +	spinlock_t portlist_lock;	/* Spin Lock for port_list */
>=20
> Comments should add new information, not just restate what the code
> already said.
>=20
> > +static inline void *tdm_get_adapdata(const struct tdm_adapter *dev) {
> > +	return dev->data;
> > +}
> > +
> > +static inline void tdm_set_adapdata(struct tdm_adapter *dev, void
> > +*data) {
> > +	dev->data =3D data;
> > +}
>=20
> Is this really needed?
>=20
> > +
> > +/* functions exported by tdm.o */
> > +
> > +extern int tdm_add_adapter(struct tdm_adapter *); extern int
> > +tdm_del_adapter(struct tdm_adapter *); extern int
> > +tdm_register_driver(struct tdm_driver *); extern void
> > +tdm_del_driver(struct tdm_driver *); extern void
> > +tdm_unregister_driver(struct tdm_driver *); extern void
> > +init_config_adapter(struct tdm_adapter *);
> > +
> > +extern unsigned int tdm_port_open(struct tdm_driver *, void **);
> > +extern unsigned int tdm_port_close(void *); extern unsigned int
> > +tdm_port_ioctl(void *, unsigned int, unsigned long); extern unsigned
> > +int tdm_channel_read(void *, void *, void *, u16 *); extern unsigned
> > +int tdm_channel_write(void *, void * , void *, u16); extern unsigned
> > +int tdm_port_poll(void *, unsigned int);
> > +
> > +extern int tdm_channel_open(u16, u16, struct tdm_port *, void **);
> > +extern int tdm_channel_close(u16, u16, struct tdm_port *,
> > +						struct tdm_channel *);
>=20
> Please provide parameter names with prototypes.
>=20
> The "extern" is unnecessary.
>=20
> > +static inline int tdm_add_driver(struct tdm_driver *driver) {
> > +	return tdm_register_driver(driver);
> > +}
>=20
> What is the semantic difference between tdm_add_driver() and
> tdm_register_driver()?  Why do they both exist?
>=20
> -Scott

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2012-04-27  2:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-10 12:57 [PATCH][2/3][RFC] TDM Framework Poonam Aggrwal
2012-04-23  8:00 ` Aggrwal Poonam-B10812
2012-04-24  0:34 ` Scott Wood
2012-04-27  2:07   ` Aggrwal Poonam-B10812
2012-04-25  5:49 ` Benjamin Herrenschmidt
2012-04-26 11:20   ` Singh Sandeep-B37400

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).