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: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
a9e2858561
commit
2e5c1ec886
|
@ -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/
|
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# 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 +=
|
|
@ -0,0 +1,575 @@
|
|||
/*!
|
||||
|
||||
\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, ¶ms, &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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef __smschar_h__
|
||||
#define __smschar_h__
|
||||
|
||||
extern int smschar_initialize(void);
|
||||
extern void smschar_terminate(void);
|
||||
|
||||
#endif // __smschar_h__
|
|
@ -0,0 +1,17 @@
|
|||
#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 diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,101 @@
|
|||
#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__
|
|
@ -0,0 +1,438 @@
|
|||
#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, ¶ms, &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");
|
|
@ -0,0 +1,21 @@
|
|||
#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__
|
|
@ -0,0 +1,447 @@
|
|||
#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, ¶ms, &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");
|
|
@ -0,0 +1,361 @@
|
|||
#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 <20> 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__
|
|
@ -0,0 +1,428 @@
|
|||
#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(¶ms, 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(¶ms, &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");
|
Loading…
Reference in New Issue