@@ -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, ¶ms,
+ &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");
+}
@@ -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__ */
@@ -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, ¶ms, &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@siano-ms.com)");
+MODULE_LICENSE("GPL");