# HG changeset patch
# User Uri Shkolnik <u...@siano-ms.com>
# Date 1231847968 -7200
# Node ID 0d667d493399447a285bac21f0f38162fbbc2241
# Parent  a9dd63fe39745548432a32617f059f2ed64b5d48
Siano sub-system

From: Uri Shkolnik <u...@siano-ms.com>

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 <u...@siano-ms.com>

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. (u...@siano-ms.com)");
+MODULE_LICENSE("GPL");



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

Reply via email to