[PATCH 1/2] Siano's SMS subsystems API - SmsHost support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Siano DTV module works with three subsystem API (DVB-API v3, DVB-API v5 (S2) and SmsHost)

Until now, only the DVB-API v3 has been supported.
The following two patch's parts add the support for the two other APIs.

The first adds the SmsHost API support. This API supports DTV standards yet to be fully supported by the DVB-API (CMMB, T-DMB and more).

The second part adds support for the DVB-API v5 ("S2")

******

This patch adds Siano subsystem, which supports the CMMB, T-DMB, DVB-H and DAB-IPD TV standards.

Signed-off-by: Uri Shkolnik <uris@xxxxxxxxxxxx>




      
This patch adds Siano subsystem, which supports the CMMB and T-DMB DTV standards.
The patch also adds Network interface (network drivers) in order to support the DVB-H and DAB-IP standards.

Signed-off-by: Uri Shkolnik <uris@xxxxxxxxxxxx>


diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smschar.c v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smschar.c
--- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smschar.c	1970-01-01 02:00:00.000000000 +0200
+++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smschar.c	2008-11-19 14:21:34.000000000 +0200
@@ -0,0 +1,652 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+#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 <linux/poll.h>
+#include <asm/system.h>		/* cli(), *_flags */
+#include <linux/uaccess.h>	/* copy_*_user */
+
+#include "smscoreapi.h"
+
+#include "smscharioctl.h"
+
+/* max number of packets allowed to be pending on queue*/
+#define SMS_CHR_MAX_Q_LEN	15
+#define SMSCHAR_NR_DEVS		7
+
+struct smschar_device_t {
+	struct cdev cdev;	/*!< Char device structure */
+	wait_queue_head_t waitq;	/* Processes waiting */
+	int cancel_waitq;
+	spinlock_t lock;	/*!< critical section */
+	int pending_count;
+	struct list_head pending_data;	/*!< list of pending data */
+	struct smscore_buffer_t *currentcb;
+	int device_index;
+	struct smscore_device_t *coredev;
+	struct smscore_client_t *smsclient;
+};
+
+/*!  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);
+
+struct smschar_device_t smschar_devices[SMSCHAR_NR_DEVS];
+static int g_smschar_inuse;
+
+static int g_pnp_status_changed = 1;
+wait_queue_head_t g_pnp_event;
+
+/**
+ * unregisters sms client and returns all queued buffers
+ *
+ * @param dev pointer to the client context (smschar parameters block)
+ *
+ */
+static void smschar_unregister_client(struct smschar_device_t *dev)
+{
+	unsigned long flags;
+
+	if (dev->coredev && dev->smsclient) {
+		dev->cancel_waitq = 1;
+		wake_up_interruptible(&dev->waitq);
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		while (!list_empty(&dev->pending_data)) {
+			struct smscore_buffer_t *cb =
+			    (struct 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.
+ */
+static int smschar_onresponse(void *context, struct smscore_buffer_t *cb)
+{
+	struct smschar_device_t *dev = context;
+	unsigned long flags;
+
+	if (!dev) {
+		sms_err("recieved bad dev pointer\n");
+		return -EFAULT;
+	}
+	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(struct SmsMsgHdr_ST);
+		cb->offset += sizeof(struct 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)
+ *
+ */
+static void smschar_onremove(void *context)
+{
+	struct smschar_device_t *dev = (struct 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.
+ */
+static int smschar_open(struct inode *inode, struct file *file)
+{
+	struct smschar_device_t *dev = container_of(inode->i_cdev,
+						    struct smschar_device_t,
+						    cdev);
+	int rc = -ENODEV;
+
+	sms_info("entering index %d\n", dev->device_index);
+	if (dev->coredev) {
+		struct 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;
+		dev->cancel_waitq = 0;
+		g_pnp_status_changed = 1;
+	}
+
+	if (rc)
+		sms_err(" exiting, rc %d\n", rc);
+
+	return rc;
+}
+
+/**
+ * unregisters client associated with the node
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ */
+static int smschar_release(struct inode *inode, struct file *file)
+{
+	smschar_unregister_client(file->private_data);
+
+	sms_info("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.
+ */
+static ssize_t smschar_read(struct file *file, char __user *buf,
+			    size_t count, loff_t *f_pos)
+{
+	struct smschar_device_t *dev = file->private_data;
+	unsigned long flags;
+	int rc, copied = 0;
+
+	if (!buf) {
+		sms_err("Bad pointer recieved from user.\n");
+		return -EFAULT;
+	}
+	if (!dev->coredev || !dev->smsclient) {
+		sms_err("no client\n");
+		return -ENODEV;
+	}
+	rc = wait_event_interruptible(dev->waitq,
+				      !list_empty(&dev->pending_data)
+				      || (dev->cancel_waitq));
+	if (rc < 0) {
+		sms_err("wait_event_interruptible error %d\n", rc);
+		return rc;
+	}
+	if (dev->cancel_waitq)
+		return 0;
+	if (!dev->smsclient) {
+		sms_err("no client\n");
+		return -ENODEV;
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+
+	while (!list_empty(&dev->pending_data) && (copied < count)) {
+		struct smscore_buffer_t *cb =
+		    (struct smscore_buffer_t *)dev->pending_data.next;
+		int actual_size = min(((int)count - copied), cb->size);
+		if (copy_to_user(&buf[copied], &((char *)cb->p)[cb->offset],
+				 actual_size)) {
+			sms_err("copy_to_user failed\n");
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return -EFAULT;
+		}
+		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.
+ */
+static ssize_t smschar_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *f_pos)
+{
+	struct smschar_device_t *dev;
+	void *buffer;
+
+	if (file == NULL) {
+		sms_err("file is NULL\n");
+		return EINVAL;
+	}
+
+	if (file->private_data == NULL) {
+		sms_err("file->private_data is NULL\n");
+		return -EINVAL;
+	}
+
+	dev = file->private_data;
+	if (!dev->smsclient) {
+		sms_err("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;
+}
+
+static int smschar_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct 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 reportedto 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.
+ */
+static int smschar_wait_get_buffer(struct smschar_device_t *dev,
+				   struct 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)
+				      || (dev->cancel_waitq));
+	if (rc < 0) {
+		sms_err("wait_event_interruptible error, rc=%d\n", rc);
+		return rc;
+	}
+	if (dev->cancel_waitq) {
+		touser->offset = 0;
+		touser->size = 0;
+		return 0;
+	}
+	if (!dev->smsclient) {
+		sms_err("no client\n");
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!list_empty(&dev->pending_data)) {
+		struct smscore_buffer_t *cb =
+		    (struct 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;
+}
+
+/**
+ * poll for data availability
+ *
+ * @param file File structure.
+ * @param wait kernel polling table.
+ *
+ * @return POLLIN flag if read data is available.
+ */
+static unsigned int smschar_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	struct smschar_device_t *dev;
+	int mask = 0;
+
+	if (file == NULL) {
+		sms_err("file is NULL\n");
+		return EINVAL;
+	}
+
+	if (file->private_data == NULL) {
+		sms_err("file->private_data is NULL\n");
+		return -EINVAL;
+	}
+
+	dev = file->private_data;
+
+	if (list_empty(&dev->pending_data)) {
+		sms_info("No data is ready, waiting for data recieve.\n");
+		poll_wait(file, &dev->waitq, wait);
+	}
+
+	if (!list_empty(&dev->pending_data))
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+
+static int smschar_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct smschar_device_t *dev = file->private_data;
+	void __user *up = (void __user *)arg;
+
+	if (!dev->coredev || !dev->smsclient) {
+		sms_err("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_IS_DEVICE_PNP_EVENT:
+		{
+			sms_info("Waiting for PnP event.\n");
+			wait_event_interruptible(g_pnp_event,
+						 !g_pnp_status_changed);
+			g_pnp_status_changed = 0;
+			sms_info("PnP Event %d.\n", g_smschar_inuse);
+			if (put_user(g_smschar_inuse, (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:
+		{
+			struct 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(struct smschar_buffer_t)))
+				return -EFAULT;
+
+			break;
+		}
+	case SMSCHAR_CANCEL_WAIT_BUFFER:
+		{
+			dev->cancel_waitq = 1;
+			wake_up_interruptible(&dev->waitq);
+			break;
+		}
+	case SMSCHAR_GET_FW_FILE_NAME:
+		{
+			if (!up)
+				return -EINVAL;
+			return smscore_get_fw_filename(dev->coredev,
+				       ((struct
+					 smschar_get_fw_filename_ioctl_t
+					 *)up)->mode,
+				       ((struct
+					 smschar_get_fw_filename_ioctl_t
+					 *)up)->filename);
+		}
+	case SMSCHAR_SEND_FW_FILE:
+		{
+			if (!up)
+				return -EINVAL;
+			return smscore_send_fw_file(dev->coredev,
+					((struct
+					smschar_send_fw_file_ioctl_t
+					*)up)->fw_buf,
+					((struct
+					smschar_send_fw_file_ioctl_t
+					*)up)->fw_size);
+		}
+
+	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,
+	.poll = smschar_poll,
+	.ioctl = smschar_ioctl,
+};
+
+static int smschar_setup_cdev(struct 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);
+	sms_info("exiting %p %d, rc %d", 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.
+ */
+static int smschar_hotplug(struct smscore_device_t *coredev,
+			   struct device *device, int arrival)
+{
+	int rc = 0, i;
+
+	sms_info("entering %d\n", arrival);
+
+	g_pnp_status_changed = 1;
+	if (arrival) {
+		/* currently only 1 instance supported */
+		if (!g_smschar_inuse) {
+			/* data notification callbacks assignment */
+			memset(smschar_devices, 0, SMSCHAR_NR_DEVS *
+			       sizeof(struct smschar_device_t));
+
+			/* Initialize each device. */
+			for (i = 0; i < SMSCHAR_NR_DEVS; i++) {
+				sms_info("create device %d", 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;
+			wake_up_interruptible(&g_pnp_event);
+		}
+	} 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);
+				sms_info("remove device %d\n", i);
+			}
+
+			g_smschar_inuse = 0;
+			wake_up_interruptible(&g_pnp_event);
+		}
+	}
+
+	sms_info("exiting, rc %d\n", rc);
+
+	return rc;		/* succeed */
+}
+
+int smschar_register(void)
+{
+	dev_t devno = MKDEV(smschar_major, smschar_minor);
+	int rc;
+
+	sms_info("registering device major=%d minor=%d\n", smschar_major,
+		 smschar_minor);
+	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) {
+		sms_warn("smschar: can't get major %d\n", smschar_major);
+		return rc;
+	}
+	init_waitqueue_head(&g_pnp_event);
+
+	return smscore_register_hotplug(smschar_hotplug);
+}
+
+void smschar_unregister(void)
+{
+	dev_t devno = MKDEV(smschar_major, smschar_minor);
+
+	unregister_chrdev_region(devno, SMSCHAR_NR_DEVS);
+	smscore_unregister_hotplug(smschar_hotplug);
+	sms_info("unregistered\n");
+}
diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smscharioctl.h v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smscharioctl.h
--- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smscharioctl.h	1970-01-01 02:00:00.000000000 +0200
+++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smscharioctl.h	2008-11-19 14:21:34.000000000 +0200
@@ -0,0 +1,52 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+#ifndef __SMS_CHAR_IOCTL_H__
+#define __SMS_CHAR_IOCTL_H__
+
+#include <linux/ioctl.h>
+
+struct smschar_buffer_t {
+	unsigned long offset;	/* offset in common buffer (mapped to user) */
+	int size;
+};
+
+struct smschar_get_fw_filename_ioctl_t {
+	int mode;
+	char filename[200];
+};
+
+struct smschar_send_fw_file_ioctl_t {
+	char *fw_buf;
+	int fw_size;
+};
+
+#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, struct smschar_buffer_t)
+#define SMSCHAR_IS_DEVICE_PNP_EVENT 	_IOR('K', 4, int)
+#define SMSCHAR_GET_FW_FILE_NAME	\
+	_IOWR('K', 5, struct smschar_get_fw_filename_ioctl_t)
+#define SMSCHAR_SEND_FW_FILE		\
+	_IOW('K', 6, struct smschar_send_fw_file_ioctl_t)
+#define SMSCHAR_CANCEL_WAIT_BUFFER	_IO('K', 7)
+
+#endif /* __SMS_CHAR_IOCTL_H__ */
diff -uNr v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsnet.c v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsnet.c
--- v4l-dvb-c5f976cab011/linux/drivers/media/dvb/siano/smsnet.c	1970-01-01 02:00:00.000000000 +0200
+++ v4l-dvb-c5f976cab011_0003/linux/drivers/media/dvb/siano/smsnet.c	2008-11-19 14:21:32.000000000 +0200
@@ -0,0 +1,455 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2006-2008, Uri Shkolnik
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#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 "smscoreapi.h"
+
+#define IPV4VERSION			0x40
+#define IPV6VERSION			0x60
+#define GETIPVERSION(_x_)	((_x_) & 0xf0)
+
+struct smsnet_client_t {
+	struct list_head entry;
+
+	struct smscore_device_t *coredev;
+	struct smscore_client_t *smsclient;
+
+	int packet_length, splitpacket_length;
+	int header_length, splitheader_length;
+	u8 splitpacket[ETH_DATA_LEN];
+};
+
+struct list_head g_smsnet_clients;
+struct mutex g_smsnet_clientslock;
+
+struct net_device *g_smsnet_device;
+struct net_device_stats g_smsnet_stats;
+
+int g_smsnet_inuse;
+
+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 {
+		/* ip6 mcast address */
+		eth[0] = 0x33;
+		eth[1] = 0x33;
+		eth[2] = buffer[36];
+		eth[3] = buffer[37];
+		eth[4] = buffer[38];
+		eth[5] = buffer[39];
+
+		eth[12] = 0x86;
+		eth[13] = 0xdd;
+	}
+
+	skb->dev = g_smsnet_device;
+	skb->protocol = eth_type_trans(skb, g_smsnet_device);
+	skb->ip_summed = CHECKSUM_COMPLETE;
+
+	g_smsnet_stats.rx_packets++;
+	g_smsnet_stats.rx_bytes += skb->len;
+
+	netif_rx(skb);
+}
+
+int check_header(struct 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, struct smscore_buffer_t *cb)
+{
+	struct smsnet_client_t *client = (struct smsnet_client_t *)context;
+	int length, rest;
+	u8 ip_ver, *buffer;
+
+	buffer = ((u8 *) cb->p) + cb->offset + sizeof(struct SmsMsgHdr_ST);
+	length = cb->size - sizeof(struct 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(struct 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((struct smsnet_client_t *)context);
+
+	kmutex_unlock(&g_smsnet_clientslock);
+}
+
+int smsnet_hotplug(struct smscore_device_t *coredev, struct device *device,
+		   int arrival)
+{
+	struct smsclient_params_t params;
+	struct smsnet_client_t *client;
+	int rc;
+
+	/* device removal handled by onremove callback */
+	if (!arrival)
+		return 0;
+
+	client = kzalloc(sizeof(struct smsnet_client_t), GFP_KERNEL);
+	if (!client) {
+		printk(KERN_INFO "%s kmalloc() failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	params.initial_id = 1;
+	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",
+		       __func__, 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", __func__);
+
+	return 0;
+}
+
+static int smsnet_open(struct net_device *dev)
+{
+	g_smsnet_inuse++;
+
+	netif_start_queue(dev);
+
+	printk(KERN_INFO "%s, %d\n", __func__, 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", __func__, g_smsnet_inuse);
+
+	return 0;
+}
+
+static int smsnet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	printk(KERN_INFO "%s\n", __func__);
+	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", __func__, 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",
+			__func__, 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;
+
+	memcpy(dev->dev_addr, "\0SIANO", ETH_ALEN);
+
+	dev->flags |= IFF_NOARP | IFF_MULTICAST | IFF_UP;
+	dev->features |= NETIF_F_IP_CSUM;
+}
+
+int smsnet_register(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", __func__);
+		return -ENOMEM;
+	}
+
+	rc = register_netdev(g_smsnet_device);
+	if (rc < 0) {
+		printk(KERN_INFO "%s register_netdev() failed %d\n",
+		       __func__, rc);
+		free_netdev(g_smsnet_device);
+		return rc;
+	}
+
+	rc = smscore_register_hotplug(smsnet_hotplug);
+
+	printk(KERN_INFO "%s, rc %d\n", __func__, rc);
+
+	return rc;
+}
+
+void smsnet_unregister(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((struct smsnet_client_t *)
+					 g_smsnet_clients.next);
+
+	kmutex_unlock(&g_smsnet_clientslock);
+
+	printk(KERN_INFO "%s\n", __func__);
+}
+
+MODULE_DESCRIPTION("Siano Network Client module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@xxxxxxxxxxxx)");
+MODULE_LICENSE("GPL");
_______________________________________________
linux-dvb mailing list
linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux