[PATCH] Siano 10226 Siano sub-system

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

 



# HG changeset patch
# User Uri Shkolnik <uris@xxxxxxxxxxxx>
# Date 1231847968 -7200
# Node ID 0d667d493399447a285bac21f0f38162fbbc2241
# Parent  a9dd63fe39745548432a32617f059f2ed64b5d48
Siano sub-system

From: 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 driver) in order to support the DVB-H and DAB-IP standards.

Priority: normal

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

diff -r a9dd63fe3974 -r 0d667d493399 linux/drivers/media/dvb/siano/smschar.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/siano/smschar.c	Tue Jan 13 13:59:28 2009 +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		17
+
+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 -r a9dd63fe3974 -r 0d667d493399 linux/drivers/media/dvb/siano/smscharioctl.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/siano/smscharioctl.h	Tue Jan 13 13:59:28 2009 +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 -r a9dd63fe3974 -r 0d667d493399 linux/drivers/media/dvb/siano/smsnet.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/siano/smsnet.c	Tue Jan 13 13:59:28 2009 +0200
@@ -0,0 +1,442 @@
+/****************************************************************
+
+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) {
+		sms_err("kmalloc() failed");
+		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) {
+		sms_err("smscore_register_client() failed %d", 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);
+	sms_info("success");
+	return 0;
+}
+
+static int smsnet_open(struct net_device *dev)
+{
+	g_smsnet_inuse++;
+
+	netif_start_queue(dev);
+	sms_info("smsnet in use %d", g_smsnet_inuse);
+	return 0;
+}
+
+static int smsnet_stop(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	g_smsnet_inuse--;
+	sms_info("smsnet in use %d", g_smsnet_inuse);
+	return 0;
+}
+
+static int smsnet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	sms_info("enter");
+	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)
+{
+	sms_info("mc count %d", dev->mc_count);
+	if (dev->mc_count) {
+		struct dev_mc_list *p;
+
+		for (p = dev->mc_list; p; p = p->next)
+			sms_info(
+			"%d %02x %02x %02x %02x %02x %02x %02x %02x",
+			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) {
+		sms_err("alloc_netdev() failed");
+		return -ENOMEM;
+	}
+
+	rc = register_netdev(g_smsnet_device);
+	if (rc < 0) {
+		sms_err("register_netdev() failed %d\n", rc);
+		free_netdev(g_smsnet_device);
+		return rc;
+	}
+
+	rc = smscore_register_hotplug(smsnet_hotplug);
+	sms_info("exit - rc %d", 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);
+
+	sms_info("exit");
+}
+
+MODULE_DESCRIPTION("Siano Network Client module");
+MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@xxxxxxxxxxxx)");
+MODULE_LICENSE("GPL");



      
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux