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

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

initial driver drop, provided by Siano Mobile Silicon, Inc.
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent a9e28585
......@@ -115,6 +115,20 @@ source "drivers/media/radio/Kconfig"
source "drivers/media/dvb/Kconfig"
#
# Mobile Digital TV devices (DVB-H, T-DMB, etc.)
#
menuconfig MDTV_ADAPTERS
bool "Mobile Digital TV adapter"
default y
if MDTV_ADAPTERS
source "drivers/media/mdtv/Kconfig"
endif # MDTV_ADAPTERS
config DAB
boolean "DAB adapters"
---help---
......
......@@ -6,3 +6,4 @@ obj-y += common/ video/
obj-$(CONFIG_VIDEO_DEV) += radio/
obj-$(CONFIG_DVB_CORE) += dvb/
obj-$(CONFIG_MDTV_ADAPTERS) += mdtv/
\ No newline at end of file
#
# Mobile Digital TV device configuration
#
config MDTV_SIANO_STELLAR_COMMON
tristate "Siano SMS10xx adapter"
default m
---help---
Choose Y here if you have SMS10xx chipset.
In order to control the SMS10xx chipset you will need SMS Host Control library.
Further documentation on this driver can be found on the WWW at
<http://www.siano-ms.com/>.
To compile this driver as a module, choose M here: the
modules will be called smschar and smsnet.
config MDTV_SIANO_STELLAR_USB
tristate "Siano SMS10xx USB dongle support"
depends on MDTV_SIANO_STELLAR_COMMON
default m
---help---
Choose Y here if you have USB dongle with SMS10xx chipset.
In order to control the SMS10xx chipset you will need SMS Host Control library.
Further documentation on this driver can be found on the WWW at
<http://www.siano-ms.com/>.
To compile this driver as a module, choose M here: the
module will be called smsusb.
#
# Makefile for the kernel MDTV driver
#
obj-$(CONFIG_MDTV_SIANO_STELLAR_COMMON) += smschar.o smsnet.o
obj-$(CONFIG_MDTV_SIANO_STELLAR_USB) += smsusb.o
EXTRA_CFLAGS +=
This diff is collapsed.
#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__
This diff is collapsed.
#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__
This diff is collapsed.
#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");
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment