V4L/DVB (8258): add support for SMS1010 and SMS1150 based digital television devices

initial driver drop, provided by Siano Mobile Silicon, Inc.

Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 7a7803b..e6a5879 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -115,6 +115,20 @@
 
 source "drivers/media/dvb/Kconfig"
 
+#
+# Mobile Digital TV devices (DVB-H, T-DMB, etc.)
+#
+menuconfig MDTV_ADAPTERS
+	bool "Mobile Digital TV adapter"
+	default y
+
+if MDTV_ADAPTERS
+
+source "drivers/media/mdtv/Kconfig"
+
+endif # MDTV_ADAPTERS
+
+
 config DAB
 	boolean "DAB adapters"
 	---help---
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 09a829d..ec2102b 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -6,3 +6,4 @@
 
 obj-$(CONFIG_VIDEO_DEV) += radio/
 obj-$(CONFIG_DVB_CORE)  += dvb/
+obj-$(CONFIG_MDTV_ADAPTERS)  += mdtv/
\ No newline at end of file
diff --git a/drivers/media/mdtv/Kconfig b/drivers/media/mdtv/Kconfig
new file mode 100644
index 0000000..f3bae45
--- /dev/null
+++ b/drivers/media/mdtv/Kconfig
@@ -0,0 +1,36 @@
+#
+# Mobile Digital TV device configuration
+#
+
+config MDTV_SIANO_STELLAR_COMMON
+	tristate "Siano SMS10xx adapter"
+	default m
+	---help---
+	  Choose Y here if you have SMS10xx chipset.
+
+	  In order to control the SMS10xx chipset you will need SMS Host Control library.
+
+	  Further documentation on this driver can be found on the WWW at
+	  <http://www.siano-ms.com/>.
+
+	  To compile this driver as a module, choose M here: the
+	  modules will be called smschar and smsnet.
+
+config MDTV_SIANO_STELLAR_USB
+	tristate "Siano SMS10xx USB dongle support"
+	depends on MDTV_SIANO_STELLAR_COMMON
+	default m
+	---help---
+	  Choose Y here if you have USB dongle with SMS10xx chipset.
+
+	  In order to control the SMS10xx chipset you will need SMS Host Control library.
+
+	  Further documentation on this driver can be found on the WWW at
+	  <http://www.siano-ms.com/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called smsusb.
+
+
+
+
diff --git a/drivers/media/mdtv/Makefile b/drivers/media/mdtv/Makefile
new file mode 100644
index 0000000..1e54d8f
--- /dev/null
+++ b/drivers/media/mdtv/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the kernel MDTV driver
+#
+
+obj-$(CONFIG_MDTV_SIANO_STELLAR_COMMON) += smschar.o smsnet.o
+obj-$(CONFIG_MDTV_SIANO_STELLAR_USB) += smsusb.o
+
+EXTRA_CFLAGS +=
diff --git a/drivers/media/mdtv/smschar.c b/drivers/media/mdtv/smschar.c
new file mode 100644
index 0000000..0477ad0
--- /dev/null
+++ b/drivers/media/mdtv/smschar.c
@@ -0,0 +1,575 @@
+/*!
+
+	\file		smschar.c
+
+	\brief		Implementation of smscore client for cdev based access
+
+    \par 		Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+
+    \par 		This program is free software; you can redistribute it and/or modify
+			it under the terms of the GNU General Public License version 3 as
+			published by the Free Software Foundation;
+
+			Software distributed under the License is distributed on an "AS
+			IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+			implied.
+
+	\author		Anatoly Greenblat
+
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>		/* printk() */
+#include <linux/fs.h>			/* everything... */
+#include <linux/types.h>		/* size_t */
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <asm/system.h>			/* cli(), *_flags */
+#include <asm/uaccess.h>		/* copy_*_user */
+
+#include "smskdefs.h" // page, scatterlist, kmutex
+#include "smscoreapi.h"
+#include "smstypes.h"
+
+#include "smscharioctl.h"
+
+#define SMS_CHR_MAX_Q_LEN	10 // max number of packets allowed to be pending on queue
+#define SMSCHAR_NR_DEVS		7
+
+typedef struct _smschar_device
+{
+	struct cdev			cdev;				//!<  Char device structure - kernel's device model representation
+
+	wait_queue_head_t	waitq;					/* Processes waiting */
+	spinlock_t			lock;				//!< critical section
+	int					pending_count;
+	struct list_head	pending_data;		//!< list of pending data
+
+	smscore_buffer_t	*currentcb;
+
+	int					device_index;
+
+	smscore_device_t	*coredev;
+	smscore_client_t	*smsclient;
+} smschar_device_t;
+
+//!  Holds the major number of the device node. may be changed at load time.
+int smschar_major = 251;
+
+//!  Holds the first minor number of the device node. may be changed at load time.
+int smschar_minor = 0;
+
+// macros that allow the load time parameters change
+module_param ( smschar_major, int, S_IRUGO );
+module_param ( smschar_minor, int, S_IRUGO );
+
+#ifdef SMSCHAR_DEBUG
+
+	#undef PERROR
+#  define PERROR(fmt, args...) printk( KERN_INFO "smschar error: line %d- %s(): " fmt,__LINE__,  __FUNCTION__, ## args)
+	#undef PWARNING
+#  define PWARNING(fmt, args...) printk( KERN_INFO "smschar warning: line %d- %s(): " fmt,__LINE__,  __FUNCTION__, ## args)
+	#undef PDEBUG					/* undef it, just in case */
+#  define PDEBUG(fmt, args...)	printk( KERN_INFO "smschar - %s(): " fmt, __FUNCTION__, ## args)
+
+#else /* not debugging: nothing */
+
+	#define PDEBUG(fmt, args...)
+	#define PERROR(fmt, args...)
+	#define PWARNING(fmt, args...)
+
+#endif
+
+smschar_device_t smschar_devices[SMSCHAR_NR_DEVS];
+static int g_smschar_inuse = 0;
+
+/**
+ * unregisters sms client and returns all queued buffers
+ *
+ * @param dev pointer to the client context (smschar parameters block)
+ *
+ */
+void smschar_unregister_client(smschar_device_t* dev)
+{
+	unsigned long flags;
+
+	if (dev->coredev && dev->smsclient)
+	{
+		wake_up_interruptible(&dev->waitq);
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		while (!list_empty(&dev->pending_data))
+		{
+			smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
+			list_del(&cb->entry);
+
+			smscore_putbuffer(dev->coredev, cb);
+
+			dev->pending_count --;
+		}
+
+		if (dev->currentcb)
+		{
+			smscore_putbuffer(dev->coredev, dev->currentcb);
+			dev->currentcb = NULL;
+			dev->pending_count --;
+		}
+
+		smscore_unregister_client(dev->smsclient);
+		dev->smsclient = NULL;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+}
+
+/**
+ * queues incoming buffers into buffers queue
+ *
+ * @param context pointer to the client context (smschar parameters block)
+ * @param cb pointer to incoming buffer descriptor
+ *
+ * @return 0 on success, <0 on queue overflow.
+ */
+int smschar_onresponse(void *context, smscore_buffer_t *cb)
+{
+	smschar_device_t *dev = context;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->pending_count > SMS_CHR_MAX_Q_LEN)
+	{
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return -EBUSY;
+	}
+
+	dev->pending_count ++;
+
+	// if data channel, remove header
+	if (dev->device_index)
+	{
+		cb->size -= sizeof(SmsMsgHdr_ST);
+		cb->offset += sizeof(SmsMsgHdr_ST);
+	}
+
+	list_add_tail(&cb->entry, &dev->pending_data);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (waitqueue_active(&dev->waitq))
+		wake_up_interruptible(&dev->waitq);
+
+	return 0;
+}
+
+/**
+ * handles device removal event
+ *
+ * @param context pointer to the client context (smschar parameters block)
+ *
+ */
+void smschar_onremove(void *context)
+{
+	smschar_device_t *dev = (smschar_device_t *) context;
+
+	smschar_unregister_client(dev);
+	dev->coredev = NULL;
+}
+
+/**
+ * registers client associated with the node
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smschar_open (struct inode *inode, struct file *file)
+{
+	smschar_device_t *dev = container_of(inode->i_cdev, smschar_device_t, cdev);
+	int rc = -ENODEV;
+
+	PDEBUG("entering index %d\n", dev->device_index);
+
+	if (dev->coredev)
+	{
+		smsclient_params_t params;
+
+		params.initial_id = dev->device_index ? dev->device_index : SMS_HOST_LIB;
+		params.data_type = dev->device_index ? MSG_SMS_DAB_CHANNEL : 0;
+		params.onresponse_handler = smschar_onresponse;
+		params.onremove_handler = smschar_onremove;
+		params.context = dev;
+
+		rc = smscore_register_client(dev->coredev, &params, &dev->smsclient);
+		if (!rc)
+		{
+			file->private_data = dev;
+		}
+	}
+
+	PDEBUG("exiting, rc %d\n", rc);
+
+	return rc;
+}
+
+/**
+ * unregisters client associated with the node
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ */
+int smschar_release(struct inode *inode, struct file *file)
+{
+	smschar_unregister_client(file->private_data);
+
+	PDEBUG("exiting\n");
+
+	return 0;
+}
+
+/**
+ * copies data from buffers in incoming queue into a user buffer
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param f_pos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+ssize_t smschar_read ( struct file * file, char __user * buf, size_t count, loff_t * f_pos )
+{
+	smschar_device_t *dev = file->private_data;
+	unsigned long flags;
+	int copied = 0;
+
+	if (!dev->coredev || !dev->smsclient)
+	{
+		PERROR("no client\n");
+		return -ENODEV;
+	}
+
+	while (copied != count)
+	{
+		if (0 > wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data)))
+		{
+			PERROR("wait_event_interruptible error\n");
+			return -ENODEV;
+		}
+
+		if (!dev->smsclient)
+		{
+			PERROR("no client\n");
+			return -ENODEV;
+		}
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		while (!list_empty(&dev->pending_data) && (copied != count))
+		{
+			smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
+			int actual_size = min(((int) count - copied), cb->size);
+
+			copy_to_user(&buf[copied], &((char*)cb->p)[cb->offset], actual_size);
+
+			copied += actual_size;
+			cb->offset += actual_size;
+			cb->size -= actual_size;
+
+			if (!cb->size)
+			{
+				list_del(&cb->entry);
+				smscore_putbuffer(dev->coredev, cb);
+
+				dev->pending_count --;
+			}
+		}
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+
+	return copied;
+}
+
+/**
+ * sends the buffer to the associated device
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param f_pos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+ssize_t smschar_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
+{
+	smschar_device_t *dev = file->private_data;
+	void *buffer;
+
+	if (!dev->smsclient)
+	{
+		PERROR("no client\n");
+		return -ENODEV;
+	}
+
+	buffer = kmalloc(ALIGN(count, SMS_ALLOC_ALIGNMENT) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+	if (buffer)
+	{
+		void *msg_buffer = (void*) SMS_ALIGN_ADDRESS(buffer);
+
+		if (!copy_from_user(msg_buffer, buf, count))
+			smsclient_sendrequest(dev->smsclient, msg_buffer, count);
+		else
+			count = 0;
+
+		kfree(buffer);
+	}
+
+	return count;
+}
+
+int smschar_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	smschar_device_t *dev = file->private_data;
+	return smscore_map_common_buffer(dev->coredev, vma);
+}
+
+/**
+ * waits until buffer inserted into a queue. when inserted buffer offset are reported
+ * to the calling process. previously reported buffer is returned to smscore pool
+ *
+ * @param dev pointer to smschar parameters block
+ * @param touser pointer to a structure that receives incoming buffer offsets
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smschar_wait_get_buffer(smschar_device_t* dev, smschar_buffer_t* touser)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->currentcb)
+	{
+		smscore_putbuffer(dev->coredev, dev->currentcb);
+		dev->currentcb = NULL;
+		dev->pending_count --;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	rc = wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data));
+	if (rc < 0)
+	{
+		PERROR("wait_event_interruptible error\n");
+		return rc;
+	}
+
+	if (!dev->smsclient)
+	{
+		PERROR("no client\n");
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!list_empty(&dev->pending_data))
+	{
+		smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
+
+		touser->offset = cb->offset_in_common + cb->offset;
+		touser->size = cb->size;
+
+		list_del(&cb->entry);
+
+		dev->currentcb = cb;
+	}
+	else
+	{
+		touser->offset = 0;
+		touser->size = 0;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+int smschar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	smschar_device_t *dev = file->private_data;
+	void __user *up = (void __user *) arg;
+
+	if (!dev->coredev || !dev->smsclient)
+	{
+		PERROR("no client\n");
+		return -ENODEV;
+	}
+
+	switch(cmd)
+	{
+		case SMSCHAR_SET_DEVICE_MODE:
+			return smscore_set_device_mode(dev->coredev, (int) arg);
+
+		case SMSCHAR_GET_DEVICE_MODE:
+		{
+			if (put_user(smscore_get_device_mode(dev->coredev), (int*) up))
+				return -EFAULT;
+
+			break;
+		}
+
+		case SMSCHAR_GET_BUFFER_SIZE:
+		{
+			if (put_user(smscore_get_common_buffer_size(dev->coredev), (int*) up))
+				return -EFAULT;
+
+			break;
+		}
+
+		case SMSCHAR_WAIT_GET_BUFFER:
+		{
+			smschar_buffer_t touser;
+			int rc;
+
+			rc = smschar_wait_get_buffer(dev, &touser);
+			if (rc < 0)
+				return rc;
+
+			if (copy_to_user(up, &touser, sizeof(smschar_buffer_t)))
+				return -EFAULT;
+
+			break;
+		}
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+struct file_operations smschar_fops =
+{
+	.owner = THIS_MODULE,
+	.read = smschar_read,
+	.write = smschar_write,
+	.open = smschar_open,
+	.release = smschar_release,
+	.mmap = smschar_mmap,
+	.ioctl = smschar_ioctl,
+};
+
+static int smschar_setup_cdev ( smschar_device_t *dev, int index )
+{
+	int rc, devno = MKDEV ( smschar_major, smschar_minor + index );
+
+	cdev_init ( &dev->cdev, &smschar_fops );
+
+	dev->cdev.owner = THIS_MODULE;
+	dev->cdev.ops = &smschar_fops;
+
+	kobject_set_name(&dev->cdev.kobj, "Siano_sms%d", index);
+
+	rc = cdev_add ( &dev->cdev, devno, 1 );
+
+	PDEBUG("exiting %p %d, rc %d\n", dev, index, rc);
+
+	return rc;
+}
+
+/**
+ * smschar callback that called when device plugged in/out. the function
+ * register or unregisters char device interface according to plug in/out
+ *
+ * @param coredev pointer to device that is being plugged in/out
+ * @param device pointer to system device object
+ * @param arrival 1 on plug-on, 0 othewise
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smschar_hotplug(smscore_device_t* coredev, struct device* device, int arrival)
+{
+	int rc = 0, i;
+
+	PDEBUG("entering %d\n", arrival);
+
+	if (arrival)
+	{
+		// currently only 1 instance supported
+		if (!g_smschar_inuse)
+		{
+			/* data notification callbacks assignment */
+			memset ( smschar_devices, 0, SMSCHAR_NR_DEVS * sizeof ( smschar_device_t ) );
+
+			/* Initialize each device. */
+			for (i = 0; i < SMSCHAR_NR_DEVS; i++)
+			{
+				smschar_setup_cdev ( &smschar_devices[i], i );
+
+				INIT_LIST_HEAD(&smschar_devices[i].pending_data);
+				spin_lock_init(&smschar_devices[i].lock);
+				init_waitqueue_head(&smschar_devices[i].waitq);
+
+				smschar_devices[i].coredev = coredev;
+				smschar_devices[i].device_index = i;
+			}
+
+			g_smschar_inuse = 1;
+		}
+	}
+	else
+	{
+		// currently only 1 instance supported
+		if (g_smschar_inuse)
+		{
+			/* Get rid of our char dev entries */
+			for(i = 0; i < SMSCHAR_NR_DEVS; i++)
+				cdev_del(&smschar_devices[i].cdev);
+
+			g_smschar_inuse = 0;
+		}
+	}
+
+	PDEBUG("exiting, rc %d\n", rc);
+
+	return rc;					/* succeed */
+}
+
+int smschar_initialize(void)
+{
+	dev_t devno = MKDEV ( smschar_major, smschar_minor );
+	int rc;
+
+	if(smschar_major)
+	{
+		rc = register_chrdev_region ( devno, SMSCHAR_NR_DEVS, "smschar" );
+	}
+	else
+	{
+		rc = alloc_chrdev_region ( &devno, smschar_minor, SMSCHAR_NR_DEVS, "smschar" );
+		smschar_major = MAJOR ( devno );
+	}
+
+	if (rc < 0)
+	{
+		PWARNING (  "smschar: can't get major %d\n", smschar_major );
+		return rc;
+	}
+
+	return smscore_register_hotplug(smschar_hotplug);
+}
+
+void smschar_terminate(void)
+{
+	dev_t devno = MKDEV ( smschar_major, smschar_minor );
+
+	unregister_chrdev_region(devno, SMSCHAR_NR_DEVS);
+	smscore_unregister_hotplug(smschar_hotplug);
+}
diff --git a/drivers/media/mdtv/smschar.h b/drivers/media/mdtv/smschar.h
new file mode 100644
index 0000000..1cd2f32
--- /dev/null
+++ b/drivers/media/mdtv/smschar.h
@@ -0,0 +1,7 @@
+#ifndef __smschar_h__
+#define __smschar_h__
+
+extern int smschar_initialize(void);
+extern void smschar_terminate(void);
+
+#endif // __smschar_h__
diff --git a/drivers/media/mdtv/smscharioctl.h b/drivers/media/mdtv/smscharioctl.h
new file mode 100644
index 0000000..e57b89ef
--- /dev/null
+++ b/drivers/media/mdtv/smscharioctl.h
@@ -0,0 +1,17 @@
+#ifndef __smscharioctl_h__
+#define __smscharioctl_h__
+
+#include <linux/ioctl.h>
+
+typedef struct _smschar_buffer_t
+{
+	unsigned long offset;		// offset in common buffer (mapped to user space)
+	int size;
+} smschar_buffer_t;
+
+#define SMSCHAR_SET_DEVICE_MODE	_IOW('K', 0, int)
+#define SMSCHAR_GET_DEVICE_MODE	_IOR('K', 1, int)
+#define SMSCHAR_GET_BUFFER_SIZE	_IOR('K', 2, int)
+#define SMSCHAR_WAIT_GET_BUFFER	_IOR('K', 3, smschar_buffer_t)
+
+#endif // __smscharioctl_h__
diff --git a/drivers/media/mdtv/smscoreapi.c b/drivers/media/mdtv/smscoreapi.c
new file mode 100644
index 0000000..a354912
--- /dev/null
+++ b/drivers/media/mdtv/smscoreapi.c
@@ -0,0 +1,1170 @@
+/*!
+
+	\file		smscoreapi.c
+
+	\brief		Siano core API module
+				This file contains implementation for the interface to sms core component
+
+    \par 		Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+
+    \par 		This program is free software; you can redistribute it and/or modify
+			it under the terms of the GNU General Public License version 3 as
+			published by the Free Software Foundation;
+
+			Software distributed under the License is distributed on an "AS
+			IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+			implied.
+
+	\author		Anatoly Greenblat
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "smskdefs.h" // device, page, scatterlist, kmutex
+
+#include <linux/firmware.h>
+
+#include "smscoreapi.h"
+#include "smstypes.h"
+
+#include "smschar.h"
+
+typedef struct _smscore_device_notifyee
+{
+	struct list_head entry;
+	hotplug_t hotplug;
+} smscore_device_notifyee_t;
+
+typedef struct _smscore_client
+{
+	struct list_head entry;
+	smscore_device_t *coredev;
+
+	void			*context;
+
+	int				data_type;
+
+	onresponse_t	onresponse_handler;
+	onremove_t		onremove_handler;
+} *psmscore_client_t;
+
+typedef struct _smscore_subclient
+{
+	struct list_head entry;
+	smscore_client_t *client;
+
+	int				id;
+} smscore_subclient_t;
+
+typedef struct _smscore_device
+{
+	struct list_head entry;
+
+	struct list_head clients;
+	struct list_head subclients;
+	spinlock_t		clientslock;
+
+	struct list_head buffers;
+	spinlock_t		bufferslock;
+	int				num_buffers;
+
+	void			*common_buffer;
+	int				common_buffer_size;
+	dma_addr_t		common_buffer_phys;
+
+	void			*context;
+	struct device	*device;
+
+	char			devpath[32];
+	unsigned long	device_flags;
+
+	setmode_t		setmode_handler;
+	detectmode_t	detectmode_handler;
+	sendrequest_t	sendrequest_handler;
+	preload_t		preload_handler;
+	postload_t		postload_handler;
+
+	int				mode, modes_supported;
+
+	struct completion version_ex_done, data_download_done, trigger_done;
+	struct completion init_device_done, reload_start_done, resume_done;
+} *psmscore_device_t;
+
+typedef struct _smscore_registry_entry
+{
+	struct list_head entry;
+	char			devpath[32];
+	int				mode;
+} smscore_registry_entry_t;
+
+struct list_head g_smscore_notifyees;
+struct list_head g_smscore_devices;
+kmutex_t g_smscore_deviceslock;
+
+struct list_head g_smscore_registry;
+kmutex_t g_smscore_registrylock;
+
+static int default_mode = 1;
+module_param(default_mode, int, 0644);
+MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
+
+int smscore_registry_getmode(char* devpath)
+{
+	smscore_registry_entry_t *entry;
+	struct list_head *next;
+
+	kmutex_lock(&g_smscore_registrylock);
+
+	for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
+	{
+		entry = (smscore_registry_entry_t *) next;
+
+		if (!strcmp(entry->devpath, devpath))
+		{
+			kmutex_unlock(&g_smscore_registrylock);
+			return entry->mode;
+		}
+	}
+
+	entry = (smscore_registry_entry_t *) kmalloc(sizeof(smscore_registry_entry_t), GFP_KERNEL);
+	if (entry)
+	{
+		entry->mode = default_mode;
+		strcpy(entry->devpath, devpath);
+
+		list_add(&entry->entry, &g_smscore_registry);
+	}
+
+	kmutex_unlock(&g_smscore_registrylock);
+
+	return default_mode;
+}
+
+void smscore_registry_setmode(char* devpath, int mode)
+{
+	smscore_registry_entry_t *entry;
+	struct list_head *next;
+
+	kmutex_lock(&g_smscore_registrylock);
+
+	for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
+	{
+		entry = (smscore_registry_entry_t *) next;
+
+		if (!strcmp(entry->devpath, devpath))
+		{
+			entry->mode = mode;
+			break;
+		}
+	}
+
+	kmutex_unlock(&g_smscore_registrylock);
+}
+
+
+void list_add_locked(struct list_head *new, struct list_head *head, spinlock_t* lock)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(lock, flags);
+
+	list_add(new, head);
+
+	spin_unlock_irqrestore(lock, flags);
+}
+
+/**
+ * register a client callback that called when device plugged in/unplugged
+ * NOTE: if devices exist callback is called immediately for each device
+ *
+ * @param hotplug callback
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_hotplug(hotplug_t hotplug)
+{
+	smscore_device_notifyee_t *notifyee;
+	struct list_head *next, *first;
+	int rc = 0;
+
+	kmutex_lock(&g_smscore_deviceslock);
+
+	notifyee = kmalloc(sizeof(smscore_device_notifyee_t), GFP_KERNEL);
+	if (notifyee)
+	{
+		// now notify callback about existing devices
+		first = &g_smscore_devices;
+		for (next = first->next; next != first && !rc; next = next->next)
+		{
+			smscore_device_t *coredev = (smscore_device_t *) next;
+			rc = hotplug(coredev, coredev->device, 1);
+		}
+
+		if (rc >= 0)
+		{
+			notifyee->hotplug = hotplug;
+			list_add(&notifyee->entry, &g_smscore_notifyees);
+		}
+		else
+			kfree(notifyee);
+	}
+	else
+		rc = -ENOMEM;
+
+	kmutex_unlock(&g_smscore_deviceslock);
+
+	return rc;
+}
+
+/**
+ * unregister a client callback that called when device plugged in/unplugged
+ *
+ * @param hotplug callback
+ *
+ */
+void smscore_unregister_hotplug(hotplug_t hotplug)
+{
+	struct list_head *next, *first;
+
+	kmutex_lock(&g_smscore_deviceslock);
+
+	first = &g_smscore_notifyees;
+
+	for (next = first->next; next != first;)
+	{
+		smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) next;
+		next = next->next;
+
+		if (notifyee->hotplug == hotplug)
+		{
+			list_del(&notifyee->entry);
+			kfree(notifyee);
+		}
+	}
+
+	kmutex_unlock(&g_smscore_deviceslock);
+}
+
+void smscore_notify_clients(smscore_device_t *coredev)
+{
+	smscore_client_t* client;
+
+	// the client must call smscore_unregister_client from remove handler
+	while (!list_empty(&coredev->clients))
+	{
+		client = (smscore_client_t *) coredev->clients.next;
+		client->onremove_handler(client->context);
+	}
+}
+
+int smscore_notify_callbacks(smscore_device_t *coredev, struct device *device, int arrival)
+{
+	struct list_head *next, *first;
+	int rc = 0;
+
+	// note: must be called under g_deviceslock
+
+	first = &g_smscore_notifyees;
+
+	for (next = first->next; next != first; next = next->next)
+	{
+		rc = ((smscore_device_notifyee_t *) next)->hotplug(coredev, device, arrival);
+		if (rc < 0)
+			break;
+	}
+
+	return rc;
+}
+
+smscore_buffer_t *smscore_createbuffer(u8* buffer, void* common_buffer, dma_addr_t common_buffer_phys)
+{
+	smscore_buffer_t *cb = kmalloc(sizeof(smscore_buffer_t), GFP_KERNEL);
+	if (!cb)
+	{
+		printk(KERN_INFO "%s kmalloc(...) failed\n", __FUNCTION__);
+		return NULL;
+	}
+
+	cb->p = buffer;
+	cb->offset_in_common = buffer - (u8*) common_buffer;
+	cb->phys = common_buffer_phys + cb->offset_in_common;
+
+	return cb;
+}
+
+/**
+ * creates coredev object for a device, prepares buffers, creates buffer mappings, notifies
+ * registered hotplugs about new device.
+ *
+ * @param params device pointer to struct with device specific parameters and handlers
+ * @param coredev pointer to a value that receives created coredev object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev)
+{
+	smscore_device_t* dev;
+	u8 *buffer;
+
+	dev = kzalloc(sizeof(smscore_device_t), GFP_KERNEL);
+	if (!dev)
+	{
+		printk(KERN_INFO "%s kzalloc(...) failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	// init list entry so it could be safe in smscore_unregister_device
+	INIT_LIST_HEAD(&dev->entry);
+
+	// init queues
+	INIT_LIST_HEAD(&dev->clients);
+	INIT_LIST_HEAD(&dev->subclients);
+	INIT_LIST_HEAD(&dev->buffers);
+
+	// init locks
+	spin_lock_init(&dev->clientslock);
+	spin_lock_init(&dev->bufferslock);
+
+	// init completion events
+	init_completion(&dev->version_ex_done);
+	init_completion(&dev->data_download_done);
+	init_completion(&dev->trigger_done);
+	init_completion(&dev->init_device_done);
+	init_completion(&dev->reload_start_done);
+	init_completion(&dev->resume_done);
+
+	// alloc common buffer
+	dev->common_buffer_size = params->buffer_size * params->num_buffers;
+	dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, &dev->common_buffer_phys, GFP_KERNEL | GFP_DMA);
+	if (!dev->common_buffer)
+	{
+		smscore_unregister_device(dev);
+		return -ENOMEM;
+	}
+
+	// prepare dma buffers
+	for (buffer = dev->common_buffer; dev->num_buffers < params->num_buffers; dev->num_buffers ++, buffer += params->buffer_size)
+	{
+		smscore_buffer_t *cb = smscore_createbuffer(buffer, dev->common_buffer, dev->common_buffer_phys);
+		if (!cb)
+		{
+			smscore_unregister_device(dev);
+			return -ENOMEM;
+		}
+
+		smscore_putbuffer(dev, cb);
+	}
+
+	printk(KERN_INFO "%s allocated %d buffers\n", __FUNCTION__, dev->num_buffers);
+
+	dev->mode = DEVICE_MODE_NONE;
+	dev->context = params->context;
+	dev->device = params->device;
+	dev->setmode_handler = params->setmode_handler;
+	dev->detectmode_handler = params->detectmode_handler;
+	dev->sendrequest_handler = params->sendrequest_handler;
+	dev->preload_handler = params->preload_handler;
+	dev->postload_handler = params->postload_handler;
+
+	dev->device_flags = params->flags;
+	strcpy(dev->devpath, params->devpath);
+
+	// add device to devices list
+	kmutex_lock(&g_smscore_deviceslock);
+	list_add(&dev->entry, &g_smscore_devices);
+	kmutex_unlock(&g_smscore_deviceslock);
+
+	*coredev = dev;
+
+	printk(KERN_INFO "%s device %p created\n", __FUNCTION__, dev);
+
+	return 0;
+}
+
+/**
+ * sets initial device mode and notifies client hotplugs that device is ready
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_start_device(smscore_device_t *coredev)
+{
+	int rc = smscore_set_device_mode(coredev, smscore_registry_getmode(coredev->devpath));
+	if (rc < 0)
+		return rc;
+
+	kmutex_lock(&g_smscore_deviceslock);
+
+	rc = smscore_notify_callbacks(coredev, coredev->device, 1);
+
+	printk(KERN_INFO "%s device %p started, rc %d\n", __FUNCTION__, coredev, rc);
+
+	kmutex_unlock(&g_smscore_deviceslock);
+
+	return rc;
+}
+
+int smscore_sendrequest_and_wait(smscore_device_t *coredev, void* buffer, size_t size, struct completion *completion)
+{
+	int rc = coredev->sendrequest_handler(coredev->context, buffer, size);
+	if (rc < 0)
+		return rc;
+
+	return wait_for_completion_timeout(completion, msecs_to_jiffies(1000)) ? 0 : -ETIME;
+}
+
+int smscore_load_firmware_family2(smscore_device_t *coredev, void *buffer, size_t size)
+{
+	SmsFirmware_ST* firmware = (SmsFirmware_ST*) buffer;
+	SmsMsgHdr_ST *msg;
+	UINT32 mem_address = firmware->StartAddress;
+	u8* payload = firmware->Payload;
+	int rc = 0;
+
+	if (coredev->preload_handler)
+	{
+		rc = coredev->preload_handler(coredev->context);
+		if (rc < 0)
+			return rc;
+	}
+
+	// PAGE_SIZE buffer shall be enough and dma aligned
+	msg = (SmsMsgHdr_ST *) kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+	if (!msg)
+		return -ENOMEM;
+
+	if (coredev->mode != DEVICE_MODE_NONE)
+	{
+		SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, sizeof(SmsMsgHdr_ST));
+		rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->reload_start_done);
+		mem_address = *(UINT32*) &payload[20];
+	}
+
+	while (size && rc >= 0)
+	{
+		SmsDataDownload_ST *DataMsg = (SmsDataDownload_ST *) msg;
+		int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);
+
+		SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, (UINT16)(sizeof(SmsMsgHdr_ST) + sizeof(UINT32) + payload_size));
+
+		DataMsg->MemAddr = mem_address;
+		memcpy(DataMsg->Payload, payload, payload_size);
+
+		if (coredev->device_flags & SMS_ROM_NO_RESPONSE && coredev->mode == DEVICE_MODE_NONE)
+			rc = coredev->sendrequest_handler(coredev->context, DataMsg, DataMsg->xMsgHeader.msgLength);
+		else
+			rc = smscore_sendrequest_and_wait(coredev, DataMsg, DataMsg->xMsgHeader.msgLength, &coredev->data_download_done);
+
+		payload += payload_size;
+		size -= payload_size;
+		mem_address += payload_size;
+	}
+
+	if (rc >= 0)
+	{
+		if (coredev->mode == DEVICE_MODE_NONE)
+		{
+			SmsMsgData_ST* TriggerMsg = (SmsMsgData_ST*) msg;
+
+			SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, sizeof(SmsMsgHdr_ST) + sizeof(UINT32) * 5);
+
+			TriggerMsg->msgData[0] = firmware->StartAddress;	// Entry point
+			TriggerMsg->msgData[1] = 5;							// Priority
+			TriggerMsg->msgData[2] = 0x200;						// Stack size
+			TriggerMsg->msgData[3] = 0;							// Parameter
+			TriggerMsg->msgData[4] = 4;							// Task ID
+
+			if (coredev->device_flags & SMS_ROM_NO_RESPONSE)
+			{
+				rc = coredev->sendrequest_handler(coredev->context, TriggerMsg, TriggerMsg->xMsgHeader.msgLength);
+				msleep(100);
+			}
+			else
+				rc = smscore_sendrequest_and_wait(coredev, TriggerMsg, TriggerMsg->xMsgHeader.msgLength, &coredev->trigger_done);
+		}
+		else
+		{
+			SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, sizeof(SmsMsgHdr_ST));
+
+			rc = coredev->sendrequest_handler(coredev->context, msg, msg->msgLength);
+		}
+	}
+
+	printk("%s %d \n", __func__, rc);
+
+	kfree(msg);
+
+	return (rc >= 0 && coredev->postload_handler) ?
+		coredev->postload_handler(coredev->context) :
+		rc;
+}
+
+/**
+ * loads specified firmware into a buffer and calls device loadfirmware_handler
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ * @param filename null-terminated string specifies firmware file name
+ * @param loadfirmware_handler device handler that loads firmware
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler)
+{
+	int rc = -ENOENT;
+
+	const struct firmware *fw;
+	u8* fw_buffer;
+
+	if (loadfirmware_handler == NULL && !(coredev->device_flags & SMS_DEVICE_FAMILY2))
+		return -EINVAL;
+
+	rc = request_firmware(&fw, filename, coredev->device);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s failed to open \"%s\"\n", __FUNCTION__, filename);
+		return rc;
+	}
+
+	fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA);
+	if (fw_buffer)
+	{
+		memcpy(fw_buffer, fw->data, fw->size);
+
+		rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
+			smscore_load_firmware_family2(coredev, fw_buffer, fw->size) :
+			loadfirmware_handler(coredev->context, fw_buffer, fw->size);
+
+		kfree(fw_buffer);
+	}
+	else
+	{
+		printk(KERN_INFO "%s failed to allocate firmware buffer\n", __FUNCTION__);
+		rc = -ENOMEM;
+	}
+
+	release_firmware(fw);
+
+	return rc;
+}
+
+/**
+ * notifies all clients registered with the device, notifies hotplugs, frees all buffers and coredev object
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ *
+ * @return 0 on success, <0 on error.
+ */
+void smscore_unregister_device(smscore_device_t *coredev)
+{
+	smscore_buffer_t *cb;
+	int num_buffers = 0;
+
+	kmutex_lock(&g_smscore_deviceslock);
+
+	smscore_notify_clients(coredev);
+	smscore_notify_callbacks(coredev, NULL, 0);
+
+	// at this point all buffers should be back
+	// onresponse must no longer be called
+
+	while (1)
+	{
+		while ((cb = smscore_getbuffer(coredev)))
+		{
+			kfree(cb);
+			num_buffers ++;
+		}
+
+		if (num_buffers == coredev->num_buffers)
+			break;
+
+		printk(KERN_INFO "%s waiting for %d buffer(s)\n", __FUNCTION__, coredev->num_buffers - num_buffers);
+		msleep(100);
+	}
+
+	printk(KERN_INFO "%s freed %d buffers\n", __FUNCTION__, num_buffers);
+
+	if (coredev->common_buffer)
+		dma_free_coherent(NULL, coredev->common_buffer_size, coredev->common_buffer, coredev->common_buffer_phys);
+
+	list_del(&coredev->entry);
+	kfree(coredev);
+
+	kmutex_unlock(&g_smscore_deviceslock);
+
+	printk(KERN_INFO "%s device %p destroyed\n", __FUNCTION__, coredev);
+}
+
+int smscore_detect_mode(smscore_device_t *coredev)
+{
+	void *buffer = kmalloc(sizeof(SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+	SmsMsgHdr_ST *msg = (SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);
+	int rc;
+
+	if (!buffer)
+		return -ENOMEM;
+
+	SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, sizeof(SmsMsgHdr_ST));
+
+	rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
+	if (rc == -ETIME)
+	{
+		printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed first try\n", __FUNCTION__);
+
+		if (wait_for_completion_timeout(&coredev->resume_done, msecs_to_jiffies(5000)))
+		{
+			rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
+			if (rc < 0)
+			{
+				printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n", __FUNCTION__, rc);
+			}
+		}
+		else
+			rc = -ETIME;
+	}
+
+	kfree(buffer);
+
+	return rc;
+}
+
+char *smscore_fw_lkup[] =
+{
+	"dvb_nova_12mhz.inp",
+	"dvb_nova_12mhz.inp",
+	"tdmb_nova.inp",
+	"none",
+	"dvb_nova_12mhz.inp",
+	"isdbt_nova_12mhz.inp",
+	"isdbt_nova_12mhz.inp",
+	"cmmb_nova_12mhz.inp",
+	"none",
+};
+
+/**
+ * calls device handler to change mode of operation
+ * NOTE: stellar/usb may disconnect when changing mode
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ * @param mode requested mode of operation
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_set_device_mode(smscore_device_t *coredev, int mode)
+{
+	void *buffer;
+	int rc = 0;
+
+	if (coredev->device_flags & SMS_DEVICE_FAMILY2)
+	{
+		if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER)
+		{
+			printk(KERN_INFO "%s invalid mode specified %d\n", __FUNCTION__, mode);
+			return -EINVAL;
+		}
+
+		if (!(coredev->device_flags & SMS_DEVICE_NOT_READY))
+		{
+			rc = smscore_detect_mode(coredev);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (coredev->mode == mode)
+		{
+			printk(KERN_INFO "%s device mode %d already set\n", __FUNCTION__, mode);
+			return 0;
+		}
+
+		if (!(coredev->modes_supported & (1 << mode)))
+		{
+			rc = smscore_load_firmware(coredev, smscore_fw_lkup[mode], NULL);
+			if (rc < 0)
+				return rc;
+		}
+		else
+		{
+			printk(KERN_INFO "%s mode %d supported by running firmware\n", __FUNCTION__, mode);
+		}
+
+		buffer = kmalloc(sizeof(SmsMsgData_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+		if (buffer)
+		{
+			SmsMsgData_ST *msg = (SmsMsgData_ST *) SMS_ALIGN_ADDRESS(buffer);
+
+			SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, sizeof(SmsMsgData_ST));
+			msg->msgData[0] = mode;
+
+			rc = smscore_sendrequest_and_wait(coredev, msg, msg->xMsgHeader.msgLength, &coredev->init_device_done);
+
+			kfree(buffer);
+		}
+		else
+			rc = -ENOMEM;
+	}
+	else
+	{
+		if (coredev->detectmode_handler)
+			coredev->detectmode_handler(coredev->context, &coredev->mode);
+
+		if (coredev->mode != mode && coredev->setmode_handler)
+			rc = coredev->setmode_handler(coredev->context, mode);
+	}
+
+	smscore_registry_setmode(coredev->devpath, mode);
+
+	if (rc >= 0)
+	{
+		coredev->mode = mode;
+		coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
+	}
+
+	return rc;
+}
+
+/**
+ * calls device handler to get current mode of operation
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ *
+ * @return current mode
+ */
+int smscore_get_device_mode(smscore_device_t *coredev)
+{
+	return coredev->mode;
+}
+
+smscore_client_t* smscore_getclient_by_type(smscore_device_t *coredev, int data_type)
+{
+	smscore_client_t *client = NULL;
+	struct list_head *next, *first;
+	unsigned long flags;
+
+	if (!data_type)
+		return NULL;
+
+	spin_lock_irqsave(&coredev->clientslock, flags);
+
+	first = &coredev->clients;
+
+	for (next = first->next; next != first; next = next->next)
+	{
+		if (((smscore_client_t*) next)->data_type == data_type)
+		{
+			client = (smscore_client_t*) next;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&coredev->clientslock, flags);
+
+	return client;
+}
+
+smscore_client_t* smscore_getclient_by_id(smscore_device_t *coredev, int id)
+{
+	smscore_client_t *client = NULL;
+	struct list_head *next, *first;
+	unsigned long flags;
+
+	spin_lock_irqsave(&coredev->clientslock, flags);
+
+	first = &coredev->subclients;
+
+	for (next = first->next; next != first; next = next->next)
+	{
+		if (((smscore_subclient_t*) next)->id == id)
+		{
+			client = ((smscore_subclient_t*) next)->client;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&coredev->clientslock, flags);
+
+	return client;
+}
+
+/**
+ * find client by response id/type, call clients onresponse handler
+ * return buffer to pool on error
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ * @param cb pointer to response buffer descriptor
+ *
+ */
+void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb)
+{
+	SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)((u8*) cb->p + cb->offset);
+	smscore_client_t * client = smscore_getclient_by_type(coredev, phdr->msgType);
+	int rc = -EBUSY;
+
+	static unsigned long last_sample_time = 0;
+	static int data_total = 0;
+	unsigned long time_now = jiffies_to_msecs(jiffies);
+
+	if (!last_sample_time)
+		last_sample_time = time_now;
+
+	if (time_now - last_sample_time > 10000)
+	{
+		printk("\n%s data rate %d bytes/secs\n", __func__, (int)((data_total * 1000) / (time_now - last_sample_time)));
+
+		last_sample_time = time_now;
+		data_total = 0;
+	}
+
+	data_total += cb->size;
+
+	if (!client)
+		client = smscore_getclient_by_id(coredev, phdr->msgDstId);
+
+	if (client)
+		rc = client->onresponse_handler(client->context, cb);
+
+	if (rc < 0)
+	{
+		switch (phdr->msgType)
+		{
+			case MSG_SMS_GET_VERSION_EX_RES:
+			{
+				SmsVersionRes_ST *ver = (SmsVersionRes_ST*) phdr;
+				printk("%s: MSG_SMS_GET_VERSION_EX_RES id %d prots 0x%x ver %d.%d\n", __FUNCTION__, ver->FirmwareId, ver->SupportedProtocols, ver->RomVersionMajor, ver->RomVersionMinor);
+
+				coredev->mode = ver->FirmwareId == 255 ? DEVICE_MODE_NONE : ver->FirmwareId;
+				coredev->modes_supported = ver->SupportedProtocols;
+
+				complete(&coredev->version_ex_done);
+				break;
+			}
+
+			case MSG_SMS_INIT_DEVICE_RES:
+				printk("%s: MSG_SMS_INIT_DEVICE_RES\n", __FUNCTION__);
+				complete(&coredev->init_device_done);
+				break;
+
+			case MSG_SW_RELOAD_START_RES:
+				printk("%s: MSG_SW_RELOAD_START_RES\n", __FUNCTION__);
+				complete(&coredev->reload_start_done);
+				break;
+
+			case MSG_SMS_DATA_DOWNLOAD_RES:
+				complete(&coredev->data_download_done);
+				break;
+
+			case MSG_SW_RELOAD_EXEC_RES:
+				printk("%s: MSG_SW_RELOAD_EXEC_RES\n", __FUNCTION__);
+				break;
+
+			case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
+				printk("%s: MSG_SMS_SWDOWNLOAD_TRIGGER_RES\n", __FUNCTION__);
+				complete(&coredev->trigger_done);
+				break;
+
+			case MSG_SMS_SLEEP_RESUME_COMP_IND:
+				complete(&coredev->resume_done);
+				break;
+
+			default:
+				printk(KERN_INFO "%s no client (%p) or error (%d), type:%d dstid:%d\n", __FUNCTION__, client, rc, phdr->msgType, phdr->msgDstId);
+		}
+
+		smscore_putbuffer(coredev, cb);
+	}
+}
+
+/**
+ * return pointer to next free buffer descriptor from core pool
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ *
+ * @return pointer to descriptor on success, NULL on error.
+ */
+smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev)
+{
+	smscore_buffer_t *cb = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&coredev->bufferslock, flags);
+
+	if (!list_empty(&coredev->buffers))
+	{
+		cb = (smscore_buffer_t *) coredev->buffers.next;
+		list_del(&cb->entry);
+	}
+
+	spin_unlock_irqrestore(&coredev->bufferslock, flags);
+
+	return cb;
+}
+
+/**
+ * return buffer descriptor to a pool
+ *
+ * @param coredev pointer to a coredev object returned by smscore_register_device
+ * @param cb pointer buffer descriptor
+ *
+ */
+void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb)
+{
+	list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
+}
+
+int smscore_validate_client(smscore_device_t *coredev, smscore_client_t *client, int id)
+{
+	smscore_client_t *existing_client;
+	smscore_subclient_t *subclient;
+
+	if (!id)
+		return 0;
+
+	existing_client = smscore_getclient_by_id(coredev, id);
+	if (existing_client == client)
+		return 0;
+
+	if (existing_client)
+		return -EBUSY;
+
+	subclient = kzalloc(sizeof(smscore_subclient_t), GFP_KERNEL);
+	if (!subclient)
+		return -ENOMEM;
+
+	subclient->client = client;
+	subclient->id = id;
+
+	list_add_locked(&subclient->entry, &coredev->subclients, &coredev->clientslock);
+
+	return 0;
+}
+
+/**
+ * creates smsclient object, check that id is taken by another client
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ * @param initial_id all messages with this id would be sent to this client
+ * @param data_type all messages of this type would be sent to this client
+ * @param onresponse_handler client handler that is called to process incoming messages
+ * @param onremove_handler client handler that is called when device is removed
+ * @param context client-specific context
+ * @param client pointer to a value that receives created smsclient object
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_register_client(smscore_device_t *coredev, smsclient_params_t *params, smscore_client_t **client)
+{
+	smscore_client_t* newclient;
+	int rc;
+
+	// check that no other channel with same data type exists
+	if (params->data_type && smscore_getclient_by_type(coredev, params->data_type))
+		return -EEXIST;
+
+	newclient = kzalloc(sizeof(smscore_client_t), GFP_KERNEL);
+	if (!newclient)
+		return -ENOMEM;
+
+	// check that no other channel with same id exists
+	rc = smscore_validate_client(coredev, newclient, params->initial_id);
+	if (rc < 0)
+	{
+		kfree(newclient);
+		return rc;
+	}
+
+	newclient->coredev = coredev;
+	newclient->data_type = params->data_type;
+	newclient->onresponse_handler = params->onresponse_handler;
+	newclient->onremove_handler = params->onremove_handler;
+	newclient->context = params->context;
+
+	list_add_locked(&newclient->entry, &coredev->clients, &coredev->clientslock);
+
+	*client = newclient;
+
+	printk(KERN_INFO "%s %p %d %d\n", __FUNCTION__, params->context, params->data_type, params->initial_id);
+
+	return 0;
+}
+
+/**
+ * frees smsclient object and all subclients associated with it
+ *
+ * @param client pointer to smsclient object returned by smscore_register_client
+ *
+ */
+void smscore_unregister_client(smscore_client_t *client)
+{
+	smscore_device_t *coredev = client->coredev;
+	struct list_head *next, *first;
+	unsigned long flags;
+
+	spin_lock_irqsave(&coredev->clientslock, flags);
+
+	first = &coredev->subclients;
+
+	for (next = first->next; next != first;)
+	{
+		smscore_subclient_t *subclient = (smscore_subclient_t *) next;
+		next = next->next;
+
+		if (subclient->client == client)
+		{
+			list_del(&subclient->entry);
+			kfree(subclient);
+		}
+	}
+
+	printk(KERN_INFO "%s %p %d\n", __FUNCTION__, client->context, client->data_type);
+
+	list_del(&client->entry);
+	kfree(client);
+
+	spin_unlock_irqrestore(&coredev->clientslock, flags);
+}
+
+/**
+ * verifies that source id is not taken by another client,
+ * calls device handler to send requests to the device
+ *
+ * @param client pointer to smsclient object returned by smscore_register_client
+ * @param buffer pointer to a request buffer
+ * @param size size (in bytes) of request buffer
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size)
+{
+	smscore_device_t* coredev = client->coredev;
+	SmsMsgHdr_ST* phdr = (SmsMsgHdr_ST*) buffer;
+
+	// check that no other channel with same id exists
+	int rc = smscore_validate_client(client->coredev, client, phdr->msgSrcId);
+	if (rc < 0)
+		return rc;
+
+	return coredev->sendrequest_handler(coredev->context, buffer, size);
+}
+
+/**
+ * return the size of large (common) buffer
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ *
+ * @return size (in bytes) of the buffer
+ */
+int smscore_get_common_buffer_size(smscore_device_t *coredev)
+{
+	return coredev->common_buffer_size;
+}
+
+/**
+ * maps common buffer (if supported by platform)
+ *
+ * @param coredev pointer to a coredev object from clients hotplug
+ * @param vma pointer to vma struct from mmap handler
+ *
+ * @return 0 on success, <0 on error.
+ */
+int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma)
+{
+	unsigned long end = vma->vm_end, start = vma->vm_start, size = PAGE_ALIGN(coredev->common_buffer_size);
+
+	if (!(vma->vm_flags & (VM_READ | VM_SHARED)) || (vma->vm_flags & VM_WRITE))
+	{
+		printk(KERN_INFO "%s invalid vm flags\n", __FUNCTION__);
+		return -EINVAL;
+	}
+
+	if ((end - start) != size)
+	{
+		printk(KERN_INFO "%s invalid size %d expected %d\n", __FUNCTION__, (int)(end - start), (int) size);
+		return -EINVAL;
+	}
+
+	if (remap_pfn_range(vma, start, coredev->common_buffer_phys >> PAGE_SHIFT, size, pgprot_noncached(vma->vm_page_prot)))
+	{
+		printk(KERN_INFO "%s remap_page_range failed\n", __FUNCTION__);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+int smscore_module_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&g_smscore_notifyees);
+	INIT_LIST_HEAD(&g_smscore_devices);
+	kmutex_init(&g_smscore_deviceslock);
+
+	INIT_LIST_HEAD(&g_smscore_registry);
+	kmutex_init(&g_smscore_registrylock);
+
+	rc = smschar_initialize();
+
+	printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
+
+	return rc;
+}
+
+void smscore_module_exit(void)
+{
+	smschar_terminate();
+
+	kmutex_lock(&g_smscore_deviceslock);
+	while (!list_empty(&g_smscore_notifyees))
+	{
+		smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) g_smscore_notifyees.next;
+
+		list_del(&notifyee->entry);
+		kfree(notifyee);
+	}
+	kmutex_unlock(&g_smscore_deviceslock);
+
+	kmutex_lock(&g_smscore_registrylock);
+	while (!list_empty(&g_smscore_registry))
+	{
+		smscore_registry_entry_t *entry = (smscore_registry_entry_t *) g_smscore_registry.next;
+
+		list_del(&entry->entry);
+		kfree(entry);
+	}
+	kmutex_unlock(&g_smscore_registrylock);
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+}
+
+module_init(smscore_module_init);
+module_exit(smscore_module_exit);
+
+MODULE_DESCRIPTION("smscore");
+MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(smscore_registry_setmode);
+EXPORT_SYMBOL(smscore_registry_getmode);
+EXPORT_SYMBOL(smscore_register_hotplug);
+EXPORT_SYMBOL(smscore_unregister_hotplug);
+EXPORT_SYMBOL(smscore_register_device);
+EXPORT_SYMBOL(smscore_unregister_device);
+EXPORT_SYMBOL(smscore_start_device);
+EXPORT_SYMBOL(smscore_load_firmware);
+EXPORT_SYMBOL(smscore_set_device_mode);
+EXPORT_SYMBOL(smscore_get_device_mode);
+EXPORT_SYMBOL(smscore_register_client);
+EXPORT_SYMBOL(smscore_unregister_client);
+EXPORT_SYMBOL(smsclient_sendrequest);
+EXPORT_SYMBOL(smscore_onresponse);
+EXPORT_SYMBOL(smscore_get_common_buffer_size);
+EXPORT_SYMBOL(smscore_map_common_buffer);
+EXPORT_SYMBOL(smscore_getbuffer);
+EXPORT_SYMBOL(smscore_putbuffer);
diff --git a/drivers/media/mdtv/smscoreapi.h b/drivers/media/mdtv/smscoreapi.h
new file mode 100644
index 0000000..a8c025f
--- /dev/null
+++ b/drivers/media/mdtv/smscoreapi.h
@@ -0,0 +1,101 @@
+#ifndef __smscoreapi_h__
+#define __smscoreapi_h__
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define SMS_ALLOC_ALIGNMENT					128
+#define SMS_DMA_ALIGNMENT					16
+#define SMS_ALIGN_ADDRESS(addr) ((((u32)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1))
+
+#define SMS_DEVICE_FAMILY2					1
+#define SMS_ROM_NO_RESPONSE					2
+#define SMS_DEVICE_NOT_READY				0x8000000
+
+typedef struct _smscore_device smscore_device_t;
+typedef struct _smscore_client smscore_client_t;
+typedef struct _smscore_buffer smscore_buffer_t;
+
+typedef int (*hotplug_t)(smscore_device_t *coredev, struct device *device, int arrival);
+
+typedef int (*setmode_t)(void *context, int mode);
+typedef void (*detectmode_t)(void *context, int *mode);
+typedef int (*sendrequest_t)(void *context, void *buffer, size_t size);
+typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size);
+typedef int (*preload_t)(void *context);
+typedef int (*postload_t)(void *context);
+
+typedef int (*onresponse_t)(void *context, smscore_buffer_t *cb);
+typedef void (*onremove_t)(void *context);
+
+typedef struct _smscore_buffer
+{
+	// public members, once passed to clients can be changed freely
+	struct list_head entry;
+	int				size;
+	int				offset;
+
+	// private members, read-only for clients
+	void			*p;
+	dma_addr_t		phys;
+	unsigned long	offset_in_common;
+} *psmscore_buffer_t;
+
+typedef struct _smsdevice_params
+{
+	struct device	*device;
+
+	int				buffer_size;
+	int				num_buffers;
+
+	char			devpath[32];
+	unsigned long	flags;
+
+	setmode_t		setmode_handler;
+	detectmode_t	detectmode_handler;
+	sendrequest_t	sendrequest_handler;
+	preload_t		preload_handler;
+	postload_t		postload_handler;
+
+	void			*context;
+} smsdevice_params_t;
+
+typedef struct _smsclient_params
+{
+	int				initial_id;
+	int				data_type;
+	onresponse_t	onresponse_handler;
+	onremove_t		onremove_handler;
+
+	void			*context;
+} smsclient_params_t;
+
+extern void smscore_registry_setmode(char *devpath, int mode);
+extern int smscore_registry_getmode(char *devpath);
+
+extern int smscore_register_hotplug(hotplug_t hotplug);
+extern void smscore_unregister_hotplug(hotplug_t hotplug);
+
+extern int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev);
+extern void smscore_unregister_device(smscore_device_t *coredev);
+
+extern int smscore_start_device(smscore_device_t *coredev);
+extern int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler);
+
+extern int smscore_set_device_mode(smscore_device_t *coredev, int mode);
+extern int smscore_get_device_mode(smscore_device_t *coredev);
+
+extern int smscore_register_client(smscore_device_t *coredev, smsclient_params_t* params, smscore_client_t **client);
+extern void smscore_unregister_client(smscore_client_t *client);
+
+extern int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size);
+extern void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb);
+
+extern int smscore_get_common_buffer_size(smscore_device_t *coredev);
+extern int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma);
+
+extern smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev);
+extern void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb);
+
+#endif // __smscoreapi_h__
diff --git a/drivers/media/mdtv/smsdvb.c b/drivers/media/mdtv/smsdvb.c
new file mode 100644
index 0000000..f2ed171
--- /dev/null
+++ b/drivers/media/mdtv/smsdvb.c
@@ -0,0 +1,438 @@
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "smskdefs.h" // page, scatterlist, kmutex
+#include "smscoreapi.h"
+#include "smstypes.h"
+
+typedef struct _smsdvb_client
+{
+	struct list_head entry;
+
+	smscore_device_t	*coredev;
+	smscore_client_t	*smsclient;
+
+	struct dvb_adapter	adapter;
+	struct dvb_demux	demux;
+	struct dmxdev		dmxdev;
+	struct dvb_frontend	frontend;
+
+	fe_status_t			fe_status;
+	int					fe_ber, fe_snr, fe_signal_strength;
+
+	struct completion	tune_done, stat_done;
+
+	// todo: save freq/band instead whole struct
+	struct dvb_frontend_parameters fe_params;
+
+} smsdvb_client_t;
+
+struct list_head g_smsdvb_clients;
+kmutex_t g_smsdvb_clientslock;
+
+int smsdvb_onresponse(void *context, smscore_buffer_t *cb)
+{
+	smsdvb_client_t *client = (smsdvb_client_t *) context;
+	SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)(((u8*) cb->p) + cb->offset);
+
+	switch(phdr->msgType)
+	{
+		case MSG_SMS_DVBT_BDA_DATA:
+			dvb_dmx_swfilter(&client->demux, (u8*)(phdr + 1), cb->size - sizeof(SmsMsgHdr_ST));
+			break;
+
+		case MSG_SMS_RF_TUNE_RES:
+			complete(&client->tune_done);
+			break;
+
+		case MSG_SMS_GET_STATISTICS_RES:
+		{
+			SmsMsgStatisticsInfo_ST* p = (SmsMsgStatisticsInfo_ST*)(phdr + 1);
+
+			if (p->Stat.IsDemodLocked)
+			{
+				client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+				client->fe_snr = p->Stat.SNR;
+				client->fe_ber = p->Stat.BER;
+
+				if (p->Stat.InBandPwr < -95)
+					client->fe_signal_strength = 0;
+				else if (p->Stat.InBandPwr > -29)
+					client->fe_signal_strength = 100;
+				else
+					client->fe_signal_strength = (p->Stat.InBandPwr + 95) * 3 / 2;
+			}
+			else
+			{
+				client->fe_status = 0;
+				client->fe_snr =
+				client->fe_ber =
+				client->fe_signal_strength = 0;
+			}
+
+			complete(&client->stat_done);
+			break;
+		}
+	}
+
+	smscore_putbuffer(client->coredev, cb);
+
+	return 0;
+}
+
+void smsdvb_unregister_client(smsdvb_client_t* client)
+{
+	// must be called under clientslock
+
+	list_del(&client->entry);
+
+	smscore_unregister_client(client->smsclient);
+	dvb_unregister_frontend(&client->frontend);
+	dvb_dmxdev_release(&client->dmxdev);
+	dvb_dmx_release(&client->demux);
+	dvb_unregister_adapter(&client->adapter);
+	kfree(client);
+}
+
+void smsdvb_onremove(void *context)
+{
+	kmutex_lock(&g_smsdvb_clientslock);
+
+	smsdvb_unregister_client((smsdvb_client_t*) context);
+
+	kmutex_unlock(&g_smsdvb_clientslock);
+}
+
+static int smsdvb_start_feed(struct dvb_demux_feed *feed)
+{
+	smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux);
+	SmsMsgData_ST PidMsg;
+
+	printk("%s add pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid);
+
+	PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+	PidMsg.xMsgHeader.msgDstId = HIF_TASK;
+	PidMsg.xMsgHeader.msgFlags = 0;
+	PidMsg.xMsgHeader.msgType  = MSG_SMS_ADD_PID_FILTER_REQ;
+	PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
+	PidMsg.msgData[0] = feed->pid;
+
+	return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg));
+}
+
+static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
+{
+	smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux);
+	SmsMsgData_ST PidMsg;
+
+	printk("%s remove pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid);
+
+	PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+	PidMsg.xMsgHeader.msgDstId = HIF_TASK;
+	PidMsg.xMsgHeader.msgFlags = 0;
+	PidMsg.xMsgHeader.msgType  = MSG_SMS_REMOVE_PID_FILTER_REQ;
+	PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
+	PidMsg.msgData[0] = feed->pid;
+
+	return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg));
+}
+
+static int smsdvb_sendrequest_and_wait(smsdvb_client_t *client, void* buffer, size_t size, struct completion *completion)
+{
+	int rc = smsclient_sendrequest(client->smsclient, buffer, size);
+	if (rc < 0)
+		return rc;
+
+	return wait_for_completion_timeout(completion, msecs_to_jiffies(2000)) ? 0 : -ETIME;
+}
+
+static int smsdvb_send_statistics_request(smsdvb_client_t *client)
+{
+	SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(SmsMsgHdr_ST), 0 };
+	return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->stat_done);
+}
+
+static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+	int rc = smsdvb_send_statistics_request(client);
+
+	if (!rc)
+		*stat = client->fe_status;
+
+	return rc;
+}
+
+static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+	int rc = smsdvb_send_statistics_request(client);
+
+	if (!rc)
+		*ber = client->fe_ber;
+
+	return rc;
+}
+
+static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+	int rc = smsdvb_send_statistics_request(client);
+
+	if (!rc)
+		*strength = client->fe_signal_strength;
+
+	return rc;
+}
+
+static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+	int rc = smsdvb_send_statistics_request(client);
+
+	if (!rc)
+		*snr = client->fe_snr;
+
+	return rc;
+}
+
+static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
+{
+	printk("%s\n", __FUNCTION__);
+
+	tune->min_delay_ms = 400;
+	tune->step_size = 250000;
+	tune->max_drift = 0;
+	return 0;
+}
+
+static int smsdvb_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+
+	struct
+	{
+		SmsMsgHdr_ST	Msg;
+		u32				Data[3];
+	} Msg;
+
+	Msg.Msg.msgSrcId  = DVBT_BDA_CONTROL_MSG_ID;
+	Msg.Msg.msgDstId  = HIF_TASK;
+	Msg.Msg.msgFlags  = 0;
+	Msg.Msg.msgType   = MSG_SMS_RF_TUNE_REQ;
+	Msg.Msg.msgLength = sizeof(Msg);
+	Msg.Data[0] = fep->frequency;
+	Msg.Data[2] = 12000000;
+
+	printk("%s freq %d band %d\n", __FUNCTION__, fep->frequency, fep->u.ofdm.bandwidth);
+
+	switch(fep->u.ofdm.bandwidth)
+	{
+		case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break;
+		case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break;
+		case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break;
+//		case BANDWIDTH_5_MHZ: Msg.Data[1] = BW_5_MHZ; break;
+		case BANDWIDTH_AUTO: return -EOPNOTSUPP;
+		default: return -EINVAL;
+	}
+
+	return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done);
+}
+
+static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
+{
+	smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
+
+	printk("%s\n", __FUNCTION__);
+
+	// todo:
+	memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters));
+	return 0;
+}
+
+static void smsdvb_release(struct dvb_frontend *fe)
+{
+	// do nothing
+}
+
+static struct dvb_frontend_ops smsdvb_fe_ops = {
+	.info = {
+		.name				= "Siano Mobile Digital SMS10xx",
+		.type				= FE_OFDM,
+		.frequency_min		= 44250000,
+		.frequency_max		= 867250000,
+		.frequency_stepsize	= 250000,
+		.caps = FE_CAN_INVERSION_AUTO |
+				FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+				FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_RECOVER |
+				FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = smsdvb_release,
+
+	.set_frontend = smsdvb_set_frontend,
+	.get_frontend = smsdvb_get_frontend,
+	.get_tune_settings = smsdvb_get_tune_settings,
+
+	.read_status = smsdvb_read_status,
+	.read_ber = smsdvb_read_ber,
+	.read_signal_strength = smsdvb_read_signal_strength,
+	.read_snr = smsdvb_read_snr,
+};
+
+int smsdvb_hotplug(smscore_device_t *coredev, struct device* device, int arrival)
+{
+	smsclient_params_t params;
+	smsdvb_client_t* client;
+	int rc;
+
+	// device removal handled by onremove callback
+	if (!arrival)
+		return 0;
+
+	if (smscore_get_device_mode(coredev) != 4)
+	{
+		rc = smscore_set_device_mode(coredev, 4);
+		if (rc < 0)
+			return rc;
+	}
+
+	client = kzalloc(sizeof(smsdvb_client_t), GFP_KERNEL);
+	if (!client)
+	{
+		printk(KERN_INFO "%s kmalloc() failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	// register dvb adapter
+	rc = dvb_register_adapter(&client->adapter, "Siano Digital Receiver", THIS_MODULE, device);
+	if (rc < 0)
+	{
+		printk("%s dvb_register_adapter() failed %d\n", __func__, rc);
+		goto adapter_error;
+	}
+
+	// init dvb demux
+	client->demux.dmx.capabilities = DMX_TS_FILTERING;
+	client->demux.filternum = 32; // todo: nova ???
+	client->demux.feednum = 32;
+	client->demux.start_feed = smsdvb_start_feed;
+	client->demux.stop_feed = smsdvb_stop_feed;
+
+	rc = dvb_dmx_init(&client->demux);
+	if (rc < 0)
+	{
+		printk("%s dvb_dmx_init failed %d\n\n", __FUNCTION__, rc);
+		goto dvbdmx_error;
+	}
+
+	// init dmxdev
+	client->dmxdev.filternum = 32;
+	client->dmxdev.demux = &client->demux.dmx;
+	client->dmxdev.capabilities = 0;
+
+	rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
+	if (rc < 0)
+	{
+		printk("%s dvb_dmxdev_init failed %d\n", __FUNCTION__, rc);
+		goto dmxdev_error;
+	}
+
+	// init and register frontend
+	memcpy(&client->frontend.ops, &smsdvb_fe_ops, sizeof(struct dvb_frontend_ops));
+
+	rc = dvb_register_frontend(&client->adapter, &client->frontend);
+	if (rc < 0)
+	{
+		printk("%s frontend registration failed %d\n", __FUNCTION__, rc);
+		goto frontend_error;
+	}
+
+	params.initial_id = 0;
+	params.data_type = MSG_SMS_DVBT_BDA_DATA;
+	params.onresponse_handler = smsdvb_onresponse;
+	params.onremove_handler = smsdvb_onremove;
+	params.context = client;
+
+	rc = smscore_register_client(coredev, &params, &client->smsclient);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s smscore_register_client() failed %d\n", __FUNCTION__, rc);
+		goto client_error;
+	}
+
+	client->coredev = coredev;
+
+	init_completion(&client->tune_done);
+	init_completion(&client->stat_done);
+
+	kmutex_lock(&g_smsdvb_clientslock);
+
+	list_add(&client->entry, &g_smsdvb_clients);
+
+	kmutex_unlock(&g_smsdvb_clientslock);
+
+	printk(KERN_INFO "%s success\n", __FUNCTION__);
+
+	return 0;
+
+client_error:
+	dvb_unregister_frontend(&client->frontend);
+
+frontend_error:
+	dvb_dmxdev_release(&client->dmxdev);
+
+dmxdev_error:
+	dvb_dmx_release(&client->demux);
+
+dvbdmx_error:
+	dvb_unregister_adapter(&client->adapter);
+
+adapter_error:
+	kfree(client);
+	return rc;
+}
+
+int smsdvb_module_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&g_smsdvb_clients);
+	kmutex_init(&g_smsdvb_clientslock);
+
+	rc = smscore_register_hotplug(smsdvb_hotplug);
+
+	printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
+
+	return rc;
+}
+
+void smsdvb_module_exit(void)
+{
+	smscore_unregister_hotplug(smsdvb_hotplug);
+
+	kmutex_lock(&g_smsdvb_clientslock);
+
+	while (!list_empty(&g_smsdvb_clients))
+		smsdvb_unregister_client((smsdvb_client_t*) g_smsdvb_clients.next);
+
+	kmutex_unlock(&g_smsdvb_clientslock);
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+}
+
+module_init(smsdvb_module_init);
+module_exit(smsdvb_module_exit);
+
+MODULE_DESCRIPTION("smsdvb dvb-api module");
+MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/mdtv/smskdefs.h b/drivers/media/mdtv/smskdefs.h
new file mode 100644
index 0000000..6874fbc
--- /dev/null
+++ b/drivers/media/mdtv/smskdefs.h
@@ -0,0 +1,21 @@
+#ifndef __smskdefs_h__
+#define __smskdefs_h__
+
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <asm/page.h>
+
+#include <linux/mutex.h>
+
+typedef struct mutex kmutex_t;
+
+#define kmutex_init(_p_) mutex_init(_p_)
+#define kmutex_lock(_p_) mutex_lock(_p_)
+#define kmutex_trylock(_p_) mutex_trylock(_p_)
+#define kmutex_unlock(_p_) mutex_unlock(_p_)
+
+
+#endif // __smskdefs_h__
diff --git a/drivers/media/mdtv/smsnet.c b/drivers/media/mdtv/smsnet.c
new file mode 100644
index 0000000..5b70d12
--- /dev/null
+++ b/drivers/media/mdtv/smsnet.c
@@ -0,0 +1,447 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>    /* struct device, and other headers */
+#include <linux/etherdevice.h>  /* eth_type_trans */
+#include <linux/ip.h>           /* struct iphdr */
+#include <linux/ipv6.h>         /* struct ipv6hdr */
+#include <linux/in.h>
+
+#include "smskdefs.h" // page, scatterlist, kmutex
+#include "smscoreapi.h"
+#include "smstypes.h"
+
+#define IPV4VERSION			0x40
+#define IPV6VERSION			0x60
+#define GETIPVERSION(_x_)	((_x_) & 0xf0)
+
+typedef struct _smsnet_client
+{
+	struct list_head entry;
+
+	smscore_device_t *coredev;
+	smscore_client_t *smsclient;
+
+	int				packet_length, splitpacket_length;
+	int				header_length, splitheader_length;
+	u8				splitpacket[ETH_DATA_LEN];
+} smsnet_client_t;
+
+struct list_head g_smsnet_clients;
+kmutex_t g_smsnet_clientslock;
+
+struct net_device *g_smsnet_device = NULL;
+struct net_device_stats g_smsnet_stats;
+
+int	g_smsnet_inuse = 0;
+
+void smsnet_send_packet(u8* buffer, int length)
+{
+	u8 *eth;
+	struct sk_buff *skb = dev_alloc_skb(length + ETH_HLEN + NET_IP_ALIGN);
+
+	if (!skb)
+	{
+		g_smsnet_stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+
+	eth = (u8 *) skb_put(skb, length + ETH_HLEN);
+	memcpy(eth + ETH_HLEN, buffer, length);
+
+	eth[6] = 0;
+	eth[7] = 1;
+	eth[8] = 1;
+	eth[9] = 3;
+	eth[10] = 4;
+	eth[11] = 5;
+
+	if (GETIPVERSION(*buffer) == IPV4VERSION)
+	{
+		eth[0] = 1;
+		eth[1] = 0;
+		eth[2] = 0x5e;
+		eth[3] = buffer[17] & 0x7f;
+		eth[4] = buffer[18];
+		eth[5] = buffer[19];
+
+		eth[12] = 0x08;
+		eth[13] = 0x00;
+	}
+	else
+	{
+		// todo: ip6 mcast address
+
+		eth[12] = 0x86;
+		eth[13] = 0xdd;
+	}
+
+	skb->dev = g_smsnet_device;
+	skb->protocol = eth_type_trans(skb, g_smsnet_device);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	g_smsnet_stats.rx_packets ++;
+	g_smsnet_stats.rx_bytes += skb->len;
+
+	netif_rx(skb);
+}
+
+int check_header(smsnet_client_t* client, u8* buffer)
+{
+	struct iphdr *ip4_hdr;
+	struct ipv6hdr *ip6_hdr;
+	struct udphdr *udp_hdr;
+	u16 csum;
+
+	// check if packet header is valid and it is a UDP
+	if (GETIPVERSION(*buffer) == IPV4VERSION)
+	{
+		ip4_hdr = (struct iphdr*) buffer;
+		csum = ip4_hdr->check;
+
+		ip4_hdr->check = 0;
+
+		// check header checksum for IPv4 packets
+		if(ip4_hdr->protocol != IPPROTO_UDP || csum != ip_fast_csum(buffer, ip4_hdr->ihl))
+		{
+			ip4_hdr->check = csum;
+			return 0;
+		}
+
+		ip4_hdr->check = csum;
+		client->packet_length = ntohs(ip4_hdr->tot_len);
+	}
+	else
+	{
+		ip6_hdr = (struct ipv6hdr *) buffer;
+		udp_hdr = (struct udphdr *)(ip6_hdr + 1);
+
+		if ((ip6_hdr->nexthdr != IPPROTO_UDP) ||
+			(ip6_hdr->payload_len != udp_hdr->len))
+		{
+			return 0;
+		}
+
+		client->packet_length = ntohs(ip6_hdr->payload_len) + sizeof(struct ipv6hdr);
+	}
+
+	// check for abnormal packet length
+	if (client->packet_length > ETH_DATA_LEN)
+		return 0;
+
+	return 1;
+}
+
+int smsnet_onresponse(void *context, smscore_buffer_t *cb)
+{
+	smsnet_client_t *client = (smsnet_client_t *) context;
+	int				length, rest;
+	u8				ip_ver, *buffer;
+
+	buffer = ((u8*) cb->p) + cb->offset + sizeof(SmsMsgHdr_ST);
+	length = cb->size - sizeof(SmsMsgHdr_ST);
+
+	if (client->splitheader_length)
+	{
+		// how much data is missing ?
+		rest = client->header_length - client->splitheader_length;
+
+		// do we have enough in this buffer ?
+		rest = min(rest, length);
+
+		memcpy(&client->splitpacket[client->splitheader_length], buffer, rest);
+
+		client->splitheader_length += rest;
+
+		if (client->splitheader_length != client->header_length)
+			goto exit;
+
+		if (check_header(client, client->splitpacket))
+		{
+			buffer += rest;
+			length -= rest;
+
+			client->splitpacket_length = client->header_length;
+		}
+
+		client->splitheader_length = 0;
+	}
+
+	if (client->splitpacket_length)
+	{
+		// how much data is missing ?
+		rest = client->packet_length - client->splitpacket_length;
+
+		// do we have enough in this buffer ?
+		rest = min(rest, length);
+
+		memcpy(&client->splitpacket[client->splitpacket_length], buffer, rest);
+
+		client->splitpacket_length += rest;
+
+		if (client->splitpacket_length != client->packet_length)
+			goto exit;
+
+		client->splitpacket_length = 0;
+
+		smsnet_send_packet(client->splitpacket, client->packet_length);
+
+		buffer += rest;
+		length -= rest;
+	}
+
+	while (length > 0)
+	{
+		ip_ver = GETIPVERSION(*buffer);
+		while (length && (ip_ver != IPV4VERSION) && (ip_ver != IPV6VERSION))
+		{
+			buffer++;
+			length--;
+			ip_ver = GETIPVERSION(*buffer);
+		}
+
+		// No more data in section
+		if (!length)
+			break;
+
+		// Set the header length at start of packet according to the version
+		// no problem with the IP header cast, since we have at least 1 byte (we use only the first byte)
+		client->header_length = (ip_ver == IPV4VERSION) ? (((struct iphdr *) buffer)->ihl * 4) : (sizeof(struct ipv6hdr) + sizeof(struct udphdr));
+
+		// Check that Header length is at least 20 (min IPv4 length)
+		if (client->header_length < 20)
+		{
+			length--;
+			buffer++;
+			continue;
+		}
+
+		// check split header case
+		if (client->header_length > length)
+		{
+			memcpy(client->splitpacket, buffer, length);
+			client->splitheader_length = length;
+			break;
+		}
+
+		if (check_header(client, buffer))
+		{
+			// check split packet case
+			if (client->packet_length > length)
+			{
+				memcpy(client->splitpacket, buffer, length);
+				client->splitpacket_length = length;
+				break;
+			}
+		}
+		else
+		{
+			length --;
+			buffer ++;
+			continue;
+		}
+
+		smsnet_send_packet(buffer, client->packet_length);
+
+		buffer += client->packet_length;
+		length -= client->packet_length;
+	}
+
+exit:
+	smscore_putbuffer(client->coredev, cb);
+
+	return 0;
+}
+
+void smsnet_unregister_client(smsnet_client_t* client)
+{
+	// must be called under clientslock
+
+	list_del(&client->entry);
+
+	smscore_unregister_client(client->smsclient);
+	kfree(client);
+}
+
+void smsnet_onremove(void *context)
+{
+	kmutex_lock(&g_smsnet_clientslock);
+
+	smsnet_unregister_client((smsnet_client_t*) context);
+
+	kmutex_unlock(&g_smsnet_clientslock);
+}
+
+int smsnet_hotplug(smscore_device_t *coredev, struct device* device, int arrival)
+{
+	smsclient_params_t params;
+	smsnet_client_t* client;
+	int rc;
+
+	// device removal handled by onremove callback
+	if (!arrival)
+		return 0;
+
+	client = kzalloc(sizeof(smsnet_client_t), GFP_KERNEL);
+	if (!client)
+	{
+		printk(KERN_INFO "%s kmalloc() failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	params.initial_id = 0;
+	params.data_type = MSG_SMS_DATA_MSG;
+	params.onresponse_handler = smsnet_onresponse;
+	params.onremove_handler = smsnet_onremove;
+	params.context = client;
+
+	rc = smscore_register_client(coredev, &params, &client->smsclient);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s smscore_register_client() failed %d\n", __FUNCTION__, rc);
+		kfree(client);
+		return rc;
+	}
+
+	client->coredev = coredev;
+
+	kmutex_lock(&g_smsnet_clientslock);
+
+	list_add(&client->entry, &g_smsnet_clients);
+
+	kmutex_unlock(&g_smsnet_clientslock);
+
+	printk(KERN_INFO "%s success\n", __FUNCTION__);
+
+	return 0;
+}
+
+static int smsnet_open(struct net_device *dev)
+{
+	g_smsnet_inuse ++;
+
+	netif_start_queue(dev);
+
+	printk(KERN_INFO "%s, %d\n", __FUNCTION__, g_smsnet_inuse);
+
+	return 0;
+}
+
+static int smsnet_stop(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+
+	g_smsnet_inuse --;
+
+	printk(KERN_INFO "%s, %d\n", __FUNCTION__, g_smsnet_inuse);
+
+	return 0;
+}
+
+static int smsnet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static struct net_device_stats * smsnet_get_stats(struct net_device *dev)
+{
+	return &g_smsnet_stats;
+}
+
+static void smsnet_set_multicast_list(struct net_device *dev)
+{
+	printk(KERN_INFO "%s %d\n", __FUNCTION__, dev->mc_count);
+	if (dev->mc_count)
+	{
+		struct dev_mc_list	*p;
+
+		for (p = dev->mc_list; p; p = p->next)
+			printk(KERN_INFO "%s %d %02x %02x %02x %02x %02x %02x %02x %02x\n", __FUNCTION__, p->dmi_addrlen,
+				p->dmi_addr[0], p->dmi_addr[1], p->dmi_addr[2], p->dmi_addr[3],
+				p->dmi_addr[4], p->dmi_addr[5], p->dmi_addr[6], p->dmi_addr[7]
+			);
+	}
+}
+
+static void smsnet_setup_device(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->open				= smsnet_open;
+	dev->stop				= smsnet_stop;
+	dev->hard_start_xmit	= smsnet_hard_start_xmit;
+	dev->get_stats			= smsnet_get_stats;
+	dev->set_multicast_list = smsnet_set_multicast_list;
+
+	dev->mc_count = 0;
+	dev->hard_header_cache  = NULL;
+
+	memcpy(dev->dev_addr, "\0SIANO", ETH_ALEN);
+
+	dev->flags |= IFF_NOARP;
+    dev->features |= NETIF_F_NO_CSUM;
+}
+
+int smsnet_module_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&g_smsnet_clients);
+	kmutex_init(&g_smsnet_clientslock);
+
+	memset(&g_smsnet_stats, 0, sizeof(g_smsnet_stats));
+
+	g_smsnet_device = alloc_netdev(0, "sms", smsnet_setup_device);
+	if (!g_smsnet_device)
+	{
+		printk(KERN_INFO "%s alloc_netdev() failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	rc = register_netdev(g_smsnet_device);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s register_netdev() failed %d\n", __FUNCTION__, rc);
+		free_netdev(g_smsnet_device);
+		return rc;
+	}
+
+	rc = smscore_register_hotplug(smsnet_hotplug);
+
+	printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
+
+	return rc;
+}
+
+void smsnet_module_exit(void)
+{
+	if (g_smsnet_device)
+	{
+		unregister_netdev(g_smsnet_device);
+		free_netdev(g_smsnet_device);
+
+		g_smsnet_device = NULL;
+	}
+
+	smscore_unregister_hotplug(smsnet_hotplug);
+
+	kmutex_lock(&g_smsnet_clientslock);
+
+	while (!list_empty(&g_smsnet_clients))
+		smsnet_unregister_client((smsnet_client_t*) g_smsnet_clients.next);
+
+	kmutex_unlock(&g_smsnet_clientslock);
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+}
+
+module_init(smsnet_module_init);
+module_exit(smsnet_module_exit);
+
+MODULE_DESCRIPTION("smsnet dvb-h ip sink module");
+MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/mdtv/smstypes.h b/drivers/media/mdtv/smstypes.h
new file mode 100644
index 0000000..011cd90
--- /dev/null
+++ b/drivers/media/mdtv/smstypes.h
@@ -0,0 +1,361 @@
+#ifndef __smstypes_h__
+#define __smstypes_h__
+
+// GPIO definitions for antenna frequency domain control (SMS8021)
+#define SMS_ANTENNA_GPIO_0					1
+#define SMS_ANTENNA_GPIO_1					0
+
+#define BW_8_MHZ							0
+#define BW_7_MHZ							1
+#define BW_6_MHZ							2
+#define BW_5_MHZ							3
+#define BW_ISDBT_1SEG						4
+#define BW_ISDBT_3SEG						5
+
+#define MSG_HDR_FLAG_SPLIT_MSG				4
+
+#define MAX_GPIO_PIN_NUMBER					31
+
+#define HIF_TASK							11
+#define SMS_HOST_LIB						150
+#define DVBT_BDA_CONTROL_MSG_ID				201
+
+#define SMS_MAX_PAYLOAD_SIZE				240
+#define SMS_TUNE_TIMEOUT					500
+
+#define MSG_SMS_GPIO_CONFIG_REQ				507
+#define MSG_SMS_GPIO_CONFIG_RES				508
+#define MSG_SMS_GPIO_SET_LEVEL_REQ			509
+#define MSG_SMS_GPIO_SET_LEVEL_RES			510
+#define MSG_SMS_GPIO_GET_LEVEL_REQ			511
+#define MSG_SMS_GPIO_GET_LEVEL_RES			512
+#define MSG_SMS_RF_TUNE_REQ					561
+#define MSG_SMS_RF_TUNE_RES					562
+#define MSG_SMS_INIT_DEVICE_REQ				578
+#define MSG_SMS_INIT_DEVICE_RES				579
+#define MSG_SMS_ADD_PID_FILTER_REQ			601
+#define MSG_SMS_ADD_PID_FILTER_RES			602
+#define MSG_SMS_REMOVE_PID_FILTER_REQ		603
+#define MSG_SMS_REMOVE_PID_FILTER_RES		604
+#define MSG_SMS_DAB_CHANNEL					607
+#define MSG_SMS_GET_PID_FILTER_LIST_REQ		608
+#define MSG_SMS_GET_PID_FILTER_LIST_RES		609
+#define MSG_SMS_GET_STATISTICS_REQ			615
+#define MSG_SMS_GET_STATISTICS_RES			616
+#define MSG_SMS_SET_ANTENNA_CONFIG_REQ		651
+#define MSG_SMS_SET_ANTENNA_CONFIG_RES		652
+#define MSG_SMS_GET_STATISTICS_EX_REQ		653
+#define MSG_SMS_GET_STATISTICS_EX_RES		654
+#define MSG_SMS_SLEEP_RESUME_COMP_IND		655
+#define MSG_SMS_DATA_DOWNLOAD_REQ			660
+#define MSG_SMS_DATA_DOWNLOAD_RES			661
+#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ		664
+#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES		665
+#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ		666
+#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES		667
+#define MSG_SMS_GET_VERSION_EX_REQ			668
+#define MSG_SMS_GET_VERSION_EX_RES			669
+#define MSG_SMS_SET_CLOCK_OUTPUT_REQ		670
+#define MSG_SMS_I2C_SET_FREQ_REQ			685
+#define MSG_SMS_GENERIC_I2C_REQ				687
+#define MSG_SMS_GENERIC_I2C_RES				688
+#define MSG_SMS_DVBT_BDA_DATA				693
+#define MSG_SW_RELOAD_REQ					697
+#define MSG_SMS_DATA_MSG					699
+#define MSG_SW_RELOAD_START_REQ				702
+#define MSG_SW_RELOAD_START_RES				703
+#define MSG_SW_RELOAD_EXEC_REQ				704
+#define MSG_SW_RELOAD_EXEC_RES				705
+#define MSG_SMS_SPI_INT_LINE_SET_REQ		710
+#define MSG_SMS_ISDBT_TUNE_REQ				776
+#define MSG_SMS_ISDBT_TUNE_RES				777
+
+#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \
+	(ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \
+	(ptr)->msgLength = len; (ptr)->msgFlags = 0; \
+} while (0)
+#define SMS_INIT_MSG(ptr, type, len) SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len)
+
+typedef enum
+{
+	DEVICE_MODE_NONE = -1,
+	DEVICE_MODE_DVBT = 0,
+	DEVICE_MODE_DVBH,
+	DEVICE_MODE_DAB_TDMB,
+	DEVICE_MODE_DAB_TDMB_DABIP,
+	DEVICE_MODE_DVBT_BDA,
+	DEVICE_MODE_ISDBT,
+	DEVICE_MODE_ISDBT_BDA,
+	DEVICE_MODE_CMMB,
+	DEVICE_MODE_RAW_TUNER,
+	DEVICE_MODE_MAX,
+} SMS_DEVICE_MODE;
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+typedef int INT32;
+
+typedef struct SmsMsgHdr_S
+{
+	UINT16	msgType;
+	UINT8	msgSrcId;
+	UINT8	msgDstId;
+	UINT16	msgLength;	// Length is of the entire message, including header
+	UINT16	msgFlags;
+} SmsMsgHdr_ST;
+
+typedef struct SmsMsgData_S
+{
+	SmsMsgHdr_ST	xMsgHeader;
+	UINT32			msgData[1];
+} SmsMsgData_ST;
+
+typedef struct SmsDataDownload_S
+{
+	SmsMsgHdr_ST	xMsgHeader;
+	UINT32			MemAddr;
+	UINT8			Payload[SMS_MAX_PAYLOAD_SIZE];
+} SmsDataDownload_ST;
+
+typedef struct SmsVersionRes_S
+{
+	SmsMsgHdr_ST	xMsgHeader;
+
+	UINT16			ChipModel;                    // e.g. 0x1102 for SMS-1102 "Nova"
+	UINT8			Step;                         // 0 - Step A
+	UINT8			MetalFix;                     // 0 - Metal 0
+
+	UINT8			FirmwareId;                   // 0xFF � ROM, otherwise the value indicated by SMSHOSTLIB_DEVICE_MODES_E
+	UINT8			SupportedProtocols;           // Bitwise OR combination of supported protocols
+
+	UINT8			VersionMajor;
+	UINT8			VersionMinor;
+	UINT8			VersionPatch;
+	UINT8			VersionFieldPatch;
+
+	UINT8			RomVersionMajor;
+	UINT8			RomVersionMinor;
+	UINT8			RomVersionPatch;
+	UINT8			RomVersionFieldPatch;
+
+	UINT8			TextLabel[34];
+} SmsVersionRes_ST;
+
+typedef struct SmsFirmware_S
+{
+	UINT32			CheckSum;
+	UINT32			Length;
+	UINT32			StartAddress;
+	UINT8			Payload[1];
+} SmsFirmware_ST;
+
+typedef struct SMSHOSTLIB_STATISTICS_S
+{
+	UINT32 Reserved;				//!< Reserved
+
+	/// Common parameters
+	UINT32 IsRfLocked;				//!< 0 - not locked, 1 - locked
+	UINT32 IsDemodLocked;			//!< 0 - not locked, 1 - locked
+	UINT32 IsExternalLNAOn;			//!< 0 - external LNA off, 1 - external LNA on
+
+	/// Reception quality
+	INT32  SNR;						//!< dB
+	UINT32 BER;						//!< Post Viterbi BER [1E-5]
+	UINT32 FIB_CRC;					//!< CRC errors percentage, valid only for DAB
+	UINT32 TS_PER;					//!< Transport stream PER, 0xFFFFFFFF indicate N/A, valid only for DVB-T/H
+	UINT32 MFER;					//!< DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H
+	INT32  RSSI;					//!< dBm
+	INT32  InBandPwr;				//!< In band power in dBM
+	INT32  CarrierOffset;			//!< Carrier Offset in bin/1024
+
+	/// Transmission parameters
+	UINT32 Frequency;				//!< Frequency in Hz
+	UINT32 Bandwidth;				//!< Bandwidth in MHz, valid only for DVB-T/H
+	UINT32 TransmissionMode;		//!< Transmission Mode, for DAB modes 1-4, for DVB-T/H FFT mode carriers in Kilos
+	UINT32 ModemState;				//!< from SMS_DvbModemState_ET , valid only for DVB-T/H
+	UINT32 GuardInterval;			//!< Guard Interval, 1 divided by value , valid only for DVB-T/H
+	UINT32 CodeRate;				//!< Code Rate from SMS_DvbModemState_ET, valid only for DVB-T/H
+	UINT32 LPCodeRate;				//!< Low Priority Code Rate from SMS_DvbModemState_ET, valid only for DVB-T/H
+	UINT32 Hierarchy;				//!< Hierarchy from SMS_Hierarchy_ET, valid only for DVB-T/H
+	UINT32 Constellation;			//!< Constellation from SMS_Constellation_ET, valid only for DVB-T/H
+
+	/// Burst parameters, valid only for DVB-H
+	UINT32 BurstSize;				//!< Current burst size in bytes, valid only for DVB-H
+	UINT32 BurstDuration;			//!< Current burst duration in mSec, valid only for DVB-H
+	UINT32 BurstCycleTime;			//!< Current burst cycle time in mSec, valid only for DVB-H
+	UINT32 CalculatedBurstCycleTime;//!< Current burst cycle time in mSec, as calculated by demodulator, valid only for DVB-H
+	UINT32 NumOfRows;				//!< Number of rows in MPE table, valid only for DVB-H
+	UINT32 NumOfPaddCols;			//!< Number of padding columns in MPE table, valid only for DVB-H
+	UINT32 NumOfPunctCols;			//!< Number of puncturing columns in MPE table, valid only for DVB-H
+	UINT32 ErrorTSPackets;			//!< Number of erroneous transport-stream packets
+	UINT32 TotalTSPackets;			//!< Total number of transport-stream packets
+	UINT32 NumOfValidMpeTlbs;		//!< Number of MPE tables which do not include errors after MPE RS decoding
+	UINT32 NumOfInvalidMpeTlbs;		//!< Number of MPE tables which include errors after MPE RS decoding
+	UINT32 NumOfCorrectedMpeTlbs;	//!< Number of MPE tables which were corrected by MPE RS decoding
+	/// Common params
+	UINT32 BERErrorCount;			//!< Number of errornous SYNC bits.
+	UINT32 BERBitCount;				//!< Total number of SYNC bits.
+
+	/// Interface information
+	UINT32 SmsToHostTxErrors;		//!< Total number of transmission errors.
+
+	/// DAB/T-DMB
+	UINT32 PreBER; 					//!< DAB/T-DMB only: Pre Viterbi BER [1E-5]
+
+	/// DVB-H TPS parameters
+	UINT32 CellId;					//!< TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered
+
+} SMSHOSTLIB_STATISTICS_ST;
+
+typedef struct
+{
+	UINT32 RequestResult;
+
+	SMSHOSTLIB_STATISTICS_ST Stat;
+
+	// Split the calc of the SNR in DAB
+	UINT32 Signal;				//!< dB
+	UINT32 Noise;				//!< dB
+
+} SmsMsgStatisticsInfo_ST;
+
+typedef struct SMSHOSTLIB_ISDBT_LAYER_STAT_S
+{
+	// Per-layer information
+	UINT32 CodeRate;			//!< Code Rate from SMSHOSTLIB_CODE_RATE_ET, 255 means layer does not exist
+	UINT32 Constellation;		//!< Constellation from SMSHOSTLIB_CONSTELLATION_ET, 255 means layer does not exist
+	UINT32 BER;					//!< Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A
+	UINT32 BERErrorCount;		//!< Post Viterbi Error Bits Count
+	UINT32 BERBitCount;			//!< Post Viterbi Total Bits Count
+	UINT32 PreBER; 				//!< Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A
+	UINT32 TS_PER;				//!< Transport stream PER [%], 0xFFFFFFFF indicate N/A
+	UINT32 ErrorTSPackets;		//!< Number of erroneous transport-stream packets
+	UINT32 TotalTSPackets;		//!< Total number of transport-stream packets
+	UINT32 TILdepthI;			//!< Time interleaver depth I parameter, 255 means layer does not exist
+	UINT32 NumberOfSegments;	//!< Number of segments in layer A, 255 means layer does not exist
+	UINT32 TMCCErrors;			//!< TMCC errors
+} SMSHOSTLIB_ISDBT_LAYER_STAT_ST;
+
+typedef struct SMSHOSTLIB_STATISTICS_ISDBT_S
+{
+	UINT32 StatisticsType;			//!< Enumerator identifying the type of the structure.  Values are the same as SMSHOSTLIB_DEVICE_MODES_E
+									//!< This fiels MUST always first in any statistics structure
+
+	UINT32 FullSize;				//!< Total size of the structure returned by the modem.  If the size requested by
+									//!< the host is smaller than FullSize, the struct will be truncated
+
+	// Common parameters
+	UINT32 IsRfLocked;				//!< 0 - not locked, 1 - locked
+	UINT32 IsDemodLocked;			//!< 0 - not locked, 1 - locked
+	UINT32 IsExternalLNAOn;			//!< 0 - external LNA off, 1 - external LNA on
+
+	// Reception quality
+	INT32  SNR;						//!< dB
+	INT32  RSSI;					//!< dBm
+	INT32  InBandPwr;				//!< In band power in dBM
+	INT32  CarrierOffset;			//!< Carrier Offset in Hz
+
+	// Transmission parameters
+	UINT32 Frequency;				//!< Frequency in Hz
+	UINT32 Bandwidth;				//!< Bandwidth in MHz
+	UINT32 TransmissionMode;		//!< ISDB-T transmission mode
+	UINT32 ModemState;				//!< 0 - Acquisition, 1 - Locked
+	UINT32 GuardInterval;			//!< Guard Interval, 1 divided by value
+	UINT32 SystemType;				//!< ISDB-T system type (ISDB-T / ISDB-Tsb)
+	UINT32 PartialReception;		//!< TRUE - partial reception, FALSE otherwise
+	UINT32 NumOfLayers;				//!< Number of ISDB-T layers in the network
+
+	// Per-layer information
+	// Layers A, B and C
+	SMSHOSTLIB_ISDBT_LAYER_STAT_ST	LayerInfo[3];	//!< Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST
+
+	// Interface information
+	UINT32 SmsToHostTxErrors;		//!< Total number of transmission errors.
+
+} SMSHOSTLIB_STATISTICS_ISDBT_ST;
+
+typedef struct SMSHOSTLIB_STATISTICS_DVB_S
+{
+	UINT32 StatisticsType;			//!< Enumerator identifying the type of the structure.  Values are the same as SMSHOSTLIB_DEVICE_MODES_E
+								//!< This fiels MUST always first in any statistics structure
+
+	UINT32 FullSize;				//!< Total size of the structure returned by the modem.  If the size requested by
+									//!< the host is smaller than FullSize, the struct will be truncated
+	// Common parameters
+	UINT32 IsRfLocked;				//!< 0 - not locked, 1 - locked
+	UINT32 IsDemodLocked;			//!< 0 - not locked, 1 - locked
+	UINT32 IsExternalLNAOn;			//!< 0 - external LNA off, 1 - external LNA on
+
+	// Reception quality
+	INT32  SNR;						//!< dB
+	UINT32 BER;						//!< Post Viterbi BER [1E-5]
+	UINT32 BERErrorCount;			//!< Number of errornous SYNC bits.
+	UINT32 BERBitCount;				//!< Total number of SYNC bits.
+	UINT32 TS_PER;					//!< Transport stream PER, 0xFFFFFFFF indicate N/A
+	UINT32 MFER;					//!< DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H
+	INT32  RSSI;					//!< dBm
+	INT32  InBandPwr;				//!< In band power in dBM
+	INT32  CarrierOffset;			//!< Carrier Offset in bin/1024
+
+	// Transmission parameters
+	UINT32 Frequency;				//!< Frequency in Hz
+	UINT32 Bandwidth;				//!< Bandwidth in MHz
+	UINT32 ModemState;				//!< from SMSHOSTLIB_DVB_MODEM_STATE_ET
+	UINT32 TransmissionMode;		//!< FFT mode carriers in Kilos
+	UINT32 GuardInterval;			//!< Guard Interval, 1 divided by value
+	UINT32 CodeRate;				//!< Code Rate from SMSHOSTLIB_CODE_RATE_ET
+	UINT32 LPCodeRate;				//!< Low Priority Code Rate from SMSHOSTLIB_CODE_RATE_ET
+	UINT32 Hierarchy;				//!< Hierarchy from SMSHOSTLIB_HIERARCHY_ET
+	UINT32 Constellation;			//!< Constellation from SMSHOSTLIB_CONSTELLATION_ET
+
+	// Burst parameters, valid only for DVB-H
+	UINT32 BurstSize;				//!< Current burst size in bytes, valid only for DVB-H
+	UINT32 BurstDuration;			//!< Current burst duration in mSec, valid only for DVB-H
+	UINT32 BurstCycleTime;			//!< Current burst cycle time in mSec, valid only for DVB-H
+	UINT32 CalculatedBurstCycleTime;//!< Current burst cycle time in mSec, as calculated by demodulator, valid only for DVB-H
+	UINT32 NumOfRows;				//!< Number of rows in MPE table, valid only for DVB-H
+	UINT32 NumOfPaddCols;			//!< Number of padding columns in MPE table, valid only for DVB-H
+	UINT32 NumOfPunctCols;			//!< Number of puncturing columns in MPE table, valid only for DVB-H
+	UINT32 ErrorTSPackets;			//!< Number of erroneous transport-stream packets
+	UINT32 TotalTSPackets;			//!< Total number of transport-stream packets
+	UINT32 NumOfValidMpeTlbs;		//!< Number of MPE tables which do not include errors after MPE RS decoding, valid only for DVB-H
+	UINT32 NumOfInvalidMpeTlbs;		//!< Number of MPE tables which include errors after MPE RS decoding, valid only for DVB-H
+	UINT32 NumOfCorrectedMpeTlbs;	//!< Number of MPE tables which were corrected by MPE RS decoding, valid only for DVB-H
+	UINT32 NumMPEReceived;			//!< DVB-H, Num MPE section received
+
+	// DVB-H TPS parameters
+	UINT32 CellId;					//!< TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered
+	UINT32 DvbhSrvIndHP;			//!< DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator
+	UINT32 DvbhSrvIndLP;			//!< DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator
+
+	// Interface information
+	UINT32 SmsToHostTxErrors;		//!< Total number of transmission errors.
+
+} SMSHOSTLIB_STATISTICS_DVB_ST;
+
+typedef struct SMSHOSTLIB_GPIO_CONFIG_S
+{
+	UINT8	Direction;				//!< GPIO direction: Input - 0, Output - 1
+	UINT8	PullUpDown;				//!< PullUp/PullDown: None - 0, PullDown - 1, PullUp - 2, Keeper - 3
+	UINT8	InputCharacteristics;	//!< Input Characteristics: Normal - 0, Schmitt trigger - 1
+	UINT8	OutputSlewRate;			//!< Output Slew Rate:	Fast slew rate - 0, Slow slew rate - 1
+	UINT8	OutputDriving;			//!< Output driving capability: 4mA - 0, 8mA - 1, 12mA - 2, 16mA - 3
+} SMSHOSTLIB_GPIO_CONFIG_ST;
+
+typedef struct SMSHOSTLIB_I2C_REQ_S
+{
+	UINT32	DeviceAddress;			// I2c device address
+	UINT32	WriteCount;				// number of bytes to write
+	UINT32	ReadCount;				// number of bytes to read
+	UINT8	Data[1];
+} SMSHOSTLIB_I2C_REQ_ST;
+
+typedef struct SMSHOSTLIB_I2C_RES_S
+{
+	UINT32	Status;					// non-zero value in case of failure
+	UINT32	ReadCount;				// number of bytes read
+	UINT8	Data[1];
+} SMSHOSTLIB_I2C_RES_ST;
+
+#endif // __smstypes_h__
diff --git a/drivers/media/mdtv/smsusb.c b/drivers/media/mdtv/smsusb.c
new file mode 100644
index 0000000..43af826
--- /dev/null
+++ b/drivers/media/mdtv/smsusb.c
@@ -0,0 +1,428 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+
+#include "smskdefs.h" // page, scatterlist, kmutex
+#include "smscoreapi.h"
+#include "smstypes.h"
+
+#define USB_VID_SIANO	0x187f
+#define USB_PID_0010	0x0010
+#define USB_PID_0100	0x0100
+#define USB_PID_0200	0x0200
+
+#define USB1_BUFFER_SIZE		0x1000
+#define USB2_BUFFER_SIZE		0x4000
+
+#define MAX_BUFFERS		50
+#define MAX_URBS		10
+
+typedef struct _smsusb_device smsusb_device_t;
+
+typedef struct _smsusb_urb
+{
+	smscore_buffer_t *cb;
+	smsusb_device_t	*dev;
+
+	struct urb		urb;
+} smsusb_urb_t;
+
+typedef struct _smsusb_device
+{
+	struct usb_device* udev;
+	smscore_device_t *coredev;
+
+	smsusb_urb_t 	surbs[MAX_URBS];
+
+	int				response_alignment;
+	int				buffer_size;
+} *psmsusb_device_t;
+
+static struct usb_device_id smsusb_id_table [] = {
+	{ USB_DEVICE(USB_VID_SIANO, USB_PID_0010) },
+	{ USB_DEVICE(USB_VID_SIANO, USB_PID_0100) },
+	{ USB_DEVICE(USB_VID_SIANO, USB_PID_0200) },
+	{ }		/* Terminating entry */
+};
+MODULE_DEVICE_TABLE (usb, smsusb_id_table);
+
+int smsusb_submit_urb(smsusb_device_t* dev, smsusb_urb_t* surb);
+
+void smsusb_onresponse(struct urb *urb)
+{
+	smsusb_urb_t *surb = (smsusb_urb_t *) urb->context;
+	smsusb_device_t *dev = surb->dev;
+
+	if (urb->status < 0)
+	{
+		printk(KERN_INFO "%s error, urb status %d, %d bytes\n", __FUNCTION__, urb->status, urb->actual_length);
+		return;
+	}
+
+	if (urb->actual_length > 0)
+	{
+		SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *) surb->cb->p;
+
+		if (urb->actual_length >= phdr->msgLength)
+		{
+			surb->cb->size = phdr->msgLength;
+
+			if (dev->response_alignment && (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG))
+			{
+				surb->cb->offset = dev->response_alignment + ((phdr->msgFlags >> 8) & 3);
+
+				// sanity check
+				if (((int) phdr->msgLength + surb->cb->offset) > urb->actual_length)
+				{
+					printk("%s: invalid response msglen %d offset %d size %d\n", __FUNCTION__, phdr->msgLength, surb->cb->offset, urb->actual_length);
+					goto exit_and_resubmit;
+				}
+
+				// move buffer pointer and copy header to its new location
+				memcpy((char*) phdr + surb->cb->offset, phdr, sizeof(SmsMsgHdr_ST));
+			}
+			else
+				surb->cb->offset = 0;
+
+			smscore_onresponse(dev->coredev, surb->cb);
+			surb->cb = NULL;
+		}
+		else
+		{
+			printk("%s invalid response msglen %d actual %d\n", __FUNCTION__, phdr->msgLength, urb->actual_length);
+		}
+	}
+
+exit_and_resubmit:
+	smsusb_submit_urb(dev, surb);
+}
+
+int smsusb_submit_urb(smsusb_device_t* dev, smsusb_urb_t* surb)
+{
+	if (!surb->cb)
+	{
+		surb->cb = smscore_getbuffer(dev->coredev);
+		if (!surb->cb)
+		{
+			printk(KERN_INFO "%s smscore_getbuffer(...) returned NULL\n", __FUNCTION__);
+			return -ENOMEM;
+		}
+	}
+
+	usb_fill_bulk_urb(
+		&surb->urb,
+		dev->udev,
+		usb_rcvbulkpipe(dev->udev, 0x81),
+		surb->cb->p,
+		dev->buffer_size,
+		smsusb_onresponse,
+		surb
+	);
+	surb->urb.transfer_dma = surb->cb->phys;
+	surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return usb_submit_urb(&surb->urb, GFP_ATOMIC);
+}
+
+void smsusb_stop_streaming(smsusb_device_t* dev)
+{
+	int i;
+
+	for (i = 0; i < MAX_URBS; i ++)
+	{
+		usb_kill_urb(&dev->surbs[i].urb);
+
+		if (dev->surbs[i].cb)
+		{
+			smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
+			dev->surbs[i].cb = NULL;
+		}
+	}
+}
+
+int smsusb_start_streaming(smsusb_device_t* dev)
+{
+	int i, rc;
+
+	for (i = 0; i < MAX_URBS; i ++)
+	{
+		rc = smsusb_submit_urb(dev, &dev->surbs[i]);
+		if (rc < 0)
+		{
+			printk(KERN_INFO "%s smsusb_submit_urb(...) failed\n", __FUNCTION__);
+			smsusb_stop_streaming(dev);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+int smsusb_sendrequest(void *context, void *buffer, size_t size)
+{
+	smsusb_device_t* dev = (smsusb_device_t*) context;
+	int dummy;
+
+	return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), buffer, size, &dummy, 1000);
+}
+
+char *smsusb1_fw_lkup[] =
+{
+	"dvbt_stellar_usb.inp",
+	"dvbh_stellar_usb.inp",
+	"tdmb_stellar_usb.inp",
+	"none",
+	"dvbt_bda_stellar_usb.inp",
+};
+
+int smsusb1_load_firmware(struct usb_device *udev, int id)
+{
+	const struct firmware *fw;
+	u8* fw_buffer;
+	int rc, dummy;
+
+	if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA)
+	{
+		printk(KERN_INFO "%s invalid firmware id specified %d\n", __FUNCTION__, id);
+		return -EINVAL;
+	}
+
+	rc = request_firmware(&fw, smsusb1_fw_lkup[id], &udev->dev);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s failed to open \"%s\" mode %d\n", __FUNCTION__, smsusb1_fw_lkup[id], id);
+		return rc;
+	}
+
+	fw_buffer = kmalloc(fw->size, GFP_KERNEL);
+	if (fw_buffer)
+	{
+		memcpy(fw_buffer, fw->data, fw->size);
+
+		rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), fw_buffer, fw->size, &dummy, 1000);
+
+		printk(KERN_INFO "%s: sent %d(%d) bytes, rc %d\n", __FUNCTION__, fw->size, dummy, rc);
+
+		kfree(fw_buffer);
+	}
+	else
+	{
+		printk(KERN_INFO "failed to allocate firmware buffer\n");
+		rc = -ENOMEM;
+	}
+
+	release_firmware(fw);
+
+	return rc;
+}
+
+void smsusb1_detectmode(void *context, int *mode)
+{
+	char *product_string = ((smsusb_device_t *) context)->udev->product;
+
+	*mode = DEVICE_MODE_NONE;
+
+	if (!product_string)
+	{
+		product_string = "none";
+		printk("%s product string not found\n", __FUNCTION__);
+	}
+	else
+	{
+		if (strstr(product_string, "DVBH"))
+			*mode = 1;
+		else if (strstr(product_string, "BDA"))
+			*mode = 4;
+		else if (strstr(product_string, "DVBT"))
+			*mode = 0;
+		else if (strstr(product_string, "TDMB"))
+			*mode = 2;
+	}
+
+	printk("%s: %d \"%s\"\n", __FUNCTION__, *mode, product_string);
+}
+
+int smsusb1_setmode(void *context, int mode)
+{
+	SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, sizeof(SmsMsgHdr_ST), 0 };
+
+	if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA)
+	{
+		printk(KERN_INFO "%s invalid firmware id specified %d\n", __FUNCTION__, mode);
+		return -EINVAL;
+	}
+
+	return smsusb_sendrequest(context, &Msg, sizeof(Msg));
+}
+
+void smsusb_term_device(struct usb_interface *intf)
+{
+	smsusb_device_t *dev = (smsusb_device_t*) usb_get_intfdata(intf);
+
+	if (dev)
+	{
+		smsusb_stop_streaming(dev);
+
+		// unregister from smscore
+		if (dev->coredev)
+			smscore_unregister_device(dev->coredev);
+
+		kfree(dev);
+
+		printk(KERN_INFO "%s device %p destroyed\n", __FUNCTION__, dev);
+	}
+
+	usb_set_intfdata(intf, NULL);
+}
+
+int smsusb_init_device(struct usb_interface *intf)
+{
+	smsdevice_params_t params;
+	smsusb_device_t* dev;
+	int i, rc;
+
+	// create device object
+	dev = kzalloc(sizeof(smsusb_device_t), GFP_KERNEL);
+	if (!dev)
+	{
+		printk(KERN_INFO "%s kzalloc(sizeof(smsusb_device_t) failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	memset(&params, 0, sizeof(params));
+	usb_set_intfdata(intf, dev);
+	dev->udev = interface_to_usbdev(intf);
+
+	switch (dev->udev->descriptor.idProduct)
+	{
+		case USB_PID_0100:
+			dev->buffer_size = USB1_BUFFER_SIZE;
+
+			params.setmode_handler = smsusb1_setmode;
+			params.detectmode_handler = smsusb1_detectmode;
+			break;
+
+		default:
+			dev->buffer_size = USB2_BUFFER_SIZE;
+			dev->response_alignment = dev->udev->ep_in[1]->desc.wMaxPacketSize - sizeof(SmsMsgHdr_ST);
+
+			params.flags |= SMS_DEVICE_FAMILY2;
+			break;
+	}
+
+	params.device = &dev->udev->dev;
+	params.buffer_size = dev->buffer_size;
+	params.num_buffers = MAX_BUFFERS;
+	params.sendrequest_handler = smsusb_sendrequest;
+	params.context = dev;
+	snprintf(params.devpath, sizeof(params.devpath), "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath);
+
+	// register in smscore
+	rc = smscore_register_device(&params, &dev->coredev);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s smscore_register_device(...) failed, rc %d\n", __FUNCTION__, rc);
+		smsusb_term_device(intf);
+		return rc;
+	}
+
+	// initialize urbs
+	for (i = 0; i < MAX_URBS; i ++)
+	{
+		dev->surbs[i].dev = dev;
+		usb_init_urb(&dev->surbs[i].urb);
+	}
+
+	rc = smsusb_start_streaming(dev);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s smsusb_start_streaming(...) failed\n", __FUNCTION__);
+		smsusb_term_device(intf);
+		return rc;
+	}
+
+	rc = smscore_start_device(dev->coredev);
+	if (rc < 0)
+	{
+		printk(KERN_INFO "%s smscore_start_device(...) failed\n", __FUNCTION__);
+		smsusb_term_device(intf);
+		return rc;
+	}
+
+	printk(KERN_INFO "%s device %p created\n", __FUNCTION__, dev);
+
+	return rc;
+}
+
+int smsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	char devpath[32];
+	int i, rc;
+
+	if (intf->num_altsetting > 0)
+	{
+		rc = usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
+		if (rc < 0)
+		{
+			printk(KERN_INFO "%s usb_set_interface failed, rc %d\n", __FUNCTION__, rc);
+			return rc;
+		}
+	}
+
+	printk(KERN_INFO "smsusb_probe %d\n", intf->cur_altsetting->desc.bInterfaceNumber);
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i ++)
+		printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, intf->cur_altsetting->endpoint[i].desc.bmAttributes, intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+	if (udev->actconfig->desc.bNumInterfaces == 2 && intf->cur_altsetting->desc.bInterfaceNumber == 0)
+	{
+		printk(KERN_INFO "rom interface 0 is not used\n");
+		return -ENODEV;
+	}
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber == 1)
+	{
+		snprintf(devpath, 32, "%d:%s", udev->bus->busnum, udev->devpath);
+		return smsusb1_load_firmware(udev, smscore_registry_getmode(devpath));
+	}
+
+	return smsusb_init_device(intf);
+}
+
+void smsusb_disconnect(struct usb_interface *intf)
+{
+	smsusb_term_device(intf);
+}
+
+static struct usb_driver smsusb_driver = {
+	.name			= "smsusb",
+	.probe			= smsusb_probe,
+	.disconnect 	= smsusb_disconnect,
+	.id_table		= smsusb_id_table,
+};
+
+int smsusb_module_init(void)
+{
+	int rc = usb_register(&smsusb_driver);
+	if (rc)
+		printk(KERN_INFO "usb_register failed. Error number %d\n", rc);
+
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+
+	return rc;
+}
+
+void smsusb_module_exit(void)
+{
+	usb_deregister(&smsusb_driver);
+	printk(KERN_INFO "%s\n", __FUNCTION__);
+}
+
+module_init(smsusb_module_init);
+module_exit(smsusb_module_exit);
+
+MODULE_DESCRIPTION("smsusb");
+MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
+MODULE_LICENSE("GPL");