-----Original Message----- From: Benjamin Herrenschmidt [mailto:b...@kernel.crashing.org] Sent: Wednesday, April 25, 2012 11:20 AM To: Aggrwal Poonam-B10812 Cc: linuxppc-dev@lists.ozlabs.org; Singh Sandeep-B37400 Subject: Re: [PATCH][2/3][RFC] TDM Framework
On Sat, 2012-03-10 at 18:27 +0530, Poonam Aggrwal wrote: > From: Sandeep Singh <sand...@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 :-) [Sandeep] Thanks for your comments. Documentation for TDM is present in the following patch: http://patchwork.ozlabs.org/patch/145857/ Regards, Sandeep 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 <sand...@freescale.com> > Signed-off-by: Poonam Aggrwal <poonam.aggr...@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 <hem...@freescale.com> > + * Rajesh Gumasta <rajesh.guma...@freescale.com> > + * > + * Modified by Sandeep Kr Singh <sand...@freescale.com> > + * Poonam Aggarwal <poonam.aggar...@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 <hem...@freescale.com> and " > + "Rajesh Gumasta <rajesh.guma...@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 <hem...@freescale.com> > + * Rajesh Gumasta <rajesh.guma...@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 */ _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev