提交 2e5c1ec8 编写于 作者: M Michael Krufky 提交者: 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: NMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: NMauro Carvalho Chehab <mchehab@infradead.org>
上级 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__
此差异已折叠。
#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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册