Commit 2e5c1ec8 authored by Michael Krufky's avatar Michael Krufky Committed by Mauro Carvalho Chehab

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

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