2008-05-22 18:05:26 -03:00
/*
* Driver for the Siano SMS10xx USB dongle
*
2008-05-22 18:29:20 -03:00
* author : Anatoly Greenblat
*
* Copyright ( c ) , 2005 - 2008 Siano Mobile Silicon , Inc .
2008-05-22 18:05:26 -03:00
*
* This program is free software ; you can redistribute it and / or modify
2008-05-22 18:29:20 -03:00
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation ;
2008-05-22 18:05:26 -03:00
*
2008-05-22 18:29:20 -03:00
* Software distributed under the License is distributed on an " AS IS "
* basis , WITHOUT WARRANTY OF ANY KIND , either express or implied .
2008-05-22 18:05:26 -03:00
*
2008-05-22 18:29:20 -03:00
* See the GNU General Public License for more details .
2008-05-22 18:05:26 -03:00
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-05-19 18:56:13 -03:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/usb.h>
# include <linux/firmware.h>
# include "smscoreapi.h"
# define USB1_BUFFER_SIZE 0x1000
# define USB2_BUFFER_SIZE 0x4000
# define MAX_BUFFERS 50
# define MAX_URBS 10
2008-06-14 18:27:18 -03:00
/* TO DO: move these to a header file */
# define USB_VID_SIANO 0x187f
# define USB_PID_STELLAR 0x0100
# define USB_PID_NOVA_A 0x0200
# define USB_PID_NOVA_B 0x0201
# define USB_PID_VEGA 0x0300
2008-05-19 18:56:13 -03:00
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
{
2008-05-06 03:11:51 -03:00
struct usb_device * udev ;
2008-05-19 18:56:13 -03:00
smscore_device_t * coredev ;
smsusb_urb_t surbs [ MAX_URBS ] ;
2008-06-15 15:14:13 -03:00
int response_alignment ;
int buffer_size ;
2008-05-19 18:56:13 -03:00
} * psmsusb_device_t ;
2008-05-06 03:11:51 -03:00
int smsusb_submit_urb ( smsusb_device_t * dev , smsusb_urb_t * surb ) ;
2008-05-19 18:56:13 -03:00
void smsusb_onresponse ( struct urb * urb )
{
smsusb_urb_t * surb = ( smsusb_urb_t * ) urb - > context ;
smsusb_device_t * dev = surb - > dev ;
2008-06-15 15:14:13 -03:00
if ( urb - > status < 0 ) {
printk ( KERN_INFO " %s error, urb status %d, %d bytes \n " ,
__func__ , urb - > status , urb - > actual_length ) ;
2008-05-19 18:56:13 -03:00
return ;
}
2008-06-15 15:14:13 -03:00
if ( urb - > actual_length > 0 ) {
2008-05-19 18:56:13 -03:00
SmsMsgHdr_ST * phdr = ( SmsMsgHdr_ST * ) surb - > cb - > p ;
2008-06-15 15:14:13 -03:00
if ( urb - > actual_length > = phdr - > msgLength ) {
2008-05-19 18:56:13 -03:00
surb - > cb - > size = phdr - > msgLength ;
2008-06-15 15:14:13 -03:00
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 ( KERN_INFO " %s: invalid "
" response msglen %d offset %d "
" size %d \n " , __func__ ,
phdr - > msgLength ,
surb - > cb - > offset ,
urb - > actual_length ) ;
2008-05-19 18:56:13 -03:00
goto exit_and_resubmit ;
}
2008-06-15 15:14:13 -03:00
/* move buffer pointer and
* copy header to its new location */
2008-05-06 03:52:44 -03:00
memcpy ( ( char * ) phdr + surb - > cb - > offset ,
phdr , sizeof ( SmsMsgHdr_ST ) ) ;
2008-06-15 15:14:13 -03:00
} else
2008-05-19 18:56:13 -03:00
surb - > cb - > offset = 0 ;
smscore_onresponse ( dev - > coredev , surb - > cb ) ;
surb - > cb = NULL ;
2008-06-15 15:14:13 -03:00
} else {
printk ( KERN_INFO " %s invalid response "
" msglen %d actual %d \n " , __func__ ,
phdr - > msgLength , urb - > actual_length ) ;
2008-05-19 18:56:13 -03:00
}
}
exit_and_resubmit :
smsusb_submit_urb ( dev , surb ) ;
}
2008-05-06 03:11:51 -03:00
int smsusb_submit_urb ( smsusb_device_t * dev , smsusb_urb_t * surb )
2008-05-19 18:56:13 -03:00
{
2008-06-15 15:14:13 -03:00
if ( ! surb - > cb ) {
2008-05-19 18:56:13 -03:00
surb - > cb = smscore_getbuffer ( dev - > coredev ) ;
2008-06-15 15:14:13 -03:00
if ( ! surb - > cb ) {
printk ( KERN_INFO " %s smscore_getbuffer(...) "
" returned NULL \n " , __func__ ) ;
2008-05-19 18:56:13 -03:00
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 ) ;
}
2008-05-06 03:11:51 -03:00
void smsusb_stop_streaming ( smsusb_device_t * dev )
2008-05-19 18:56:13 -03:00
{
int i ;
2008-06-15 15:14:13 -03:00
for ( i = 0 ; i < MAX_URBS ; i + + ) {
2008-05-19 18:56:13 -03:00
usb_kill_urb ( & dev - > surbs [ i ] . urb ) ;
2008-06-15 15:14:13 -03:00
if ( dev - > surbs [ i ] . cb ) {
2008-05-19 18:56:13 -03:00
smscore_putbuffer ( dev - > coredev , dev - > surbs [ i ] . cb ) ;
dev - > surbs [ i ] . cb = NULL ;
}
}
}
2008-05-06 03:11:51 -03:00
int smsusb_start_streaming ( smsusb_device_t * dev )
2008-05-19 18:56:13 -03:00
{
int i , rc ;
2008-06-15 15:14:13 -03:00
for ( i = 0 ; i < MAX_URBS ; i + + ) {
2008-05-19 18:56:13 -03:00
rc = smsusb_submit_urb ( dev , & dev - > surbs [ i ] ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s smsusb_submit_urb(...) "
" failed \n " , __func__ ) ;
2008-05-19 18:56:13 -03:00
smsusb_stop_streaming ( dev ) ;
break ;
}
}
return rc ;
}
int smsusb_sendrequest ( void * context , void * buffer , size_t size )
{
2008-05-06 03:11:51 -03:00
smsusb_device_t * dev = ( smsusb_device_t * ) context ;
2008-05-19 18:56:13 -03:00
int dummy ;
2008-06-15 15:14:13 -03:00
return usb_bulk_msg ( dev - > udev , usb_sndbulkpipe ( dev - > udev , 2 ) ,
buffer , size , & dummy , 1000 ) ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 15:14:13 -03:00
char * smsusb1_fw_lkup [ ] = {
2008-05-19 18:56:13 -03:00
" 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 ;
2008-05-06 03:11:51 -03:00
u8 * fw_buffer ;
2008-05-19 18:56:13 -03:00
int rc , dummy ;
2008-06-15 15:14:13 -03:00
if ( id < DEVICE_MODE_DVBT | | id > DEVICE_MODE_DVBT_BDA ) {
printk ( KERN_INFO " %s invalid firmware id specified %d \n " ,
__func__ , id ) ;
2008-05-19 18:56:13 -03:00
return - EINVAL ;
}
rc = request_firmware ( & fw , smsusb1_fw_lkup [ id ] , & udev - > dev ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s failed to open \" %s \" mode %d \n " ,
__func__ , smsusb1_fw_lkup [ id ] , id ) ;
2008-05-19 18:56:13 -03:00
return rc ;
}
fw_buffer = kmalloc ( fw - > size , GFP_KERNEL ) ;
2008-06-15 15:14:13 -03:00
if ( fw_buffer ) {
2008-05-19 18:56:13 -03:00
memcpy ( fw_buffer , fw - > data , fw - > size ) ;
2008-06-15 15:14:13 -03:00
rc = usb_bulk_msg ( udev , usb_sndbulkpipe ( udev , 2 ) ,
fw_buffer , fw - > size , & dummy , 1000 ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
printk ( KERN_INFO " %s: sent %d(%d) bytes, rc %d \n " ,
__func__ , fw - > size , dummy , rc ) ;
2008-05-19 18:56:13 -03:00
kfree ( fw_buffer ) ;
2008-06-15 15:14:13 -03:00
} else {
2008-05-19 18:56:13 -03:00
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 ;
2008-06-15 15:14:13 -03:00
if ( ! product_string ) {
2008-05-19 18:56:13 -03:00
product_string = " none " ;
2008-05-22 18:30:17 -03:00
printk ( " %s product string not found \n " , __func__ ) ;
2008-06-15 15:14:13 -03:00
} 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 ;
2008-05-19 18:56:13 -03:00
2008-05-22 18:30:17 -03:00
printk ( " %s: %d \" %s \" \n " , __func__ , * mode , product_string ) ;
2008-05-19 18:56:13 -03:00
}
int smsusb1_setmode ( void * context , int mode )
{
2008-06-15 15:14:13 -03:00
SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ , 0 , HIF_TASK ,
sizeof ( SmsMsgHdr_ST ) , 0 } ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
if ( mode < DEVICE_MODE_DVBT | | mode > DEVICE_MODE_DVBT_BDA ) {
printk ( KERN_INFO " %s invalid firmware id specified %d \n " ,
__func__ , mode ) ;
2008-05-19 18:56:13 -03:00
return - EINVAL ;
}
return smsusb_sendrequest ( context , & Msg , sizeof ( Msg ) ) ;
}
void smsusb_term_device ( struct usb_interface * intf )
{
2008-05-06 03:52:44 -03:00
smsusb_device_t * dev = ( smsusb_device_t * ) usb_get_intfdata ( intf ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
if ( dev ) {
2008-05-19 18:56:13 -03:00
smsusb_stop_streaming ( dev ) ;
// unregister from smscore
if ( dev - > coredev )
smscore_unregister_device ( dev - > coredev ) ;
kfree ( dev ) ;
2008-05-22 18:30:17 -03:00
printk ( KERN_INFO " %s device %p destroyed \n " , __func__ , dev ) ;
2008-05-19 18:56:13 -03:00
}
usb_set_intfdata ( intf , NULL ) ;
}
int smsusb_init_device ( struct usb_interface * intf )
{
smsdevice_params_t params ;
2008-05-06 03:11:51 -03:00
smsusb_device_t * dev ;
2008-05-19 18:56:13 -03:00
int i , rc ;
// create device object
dev = kzalloc ( sizeof ( smsusb_device_t ) , GFP_KERNEL ) ;
2008-06-15 15:14:13 -03:00
if ( ! dev ) {
printk ( KERN_INFO " %s kzalloc(sizeof(smsusb_device_t) failed \n " ,
__func__ ) ;
2008-05-19 18:56:13 -03:00
return - ENOMEM ;
}
memset ( & params , 0 , sizeof ( params ) ) ;
usb_set_intfdata ( intf , dev ) ;
dev - > udev = interface_to_usbdev ( intf ) ;
2008-06-14 00:43:26 -03:00
switch ( dev - > udev - > descriptor . idProduct ) {
2008-06-14 18:27:18 -03:00
case USB_PID_STELLAR :
2008-06-14 00:43:26 -03:00
dev - > buffer_size = USB1_BUFFER_SIZE ;
params . setmode_handler = smsusb1_setmode ;
params . detectmode_handler = smsusb1_detectmode ;
params . device_type = SMS_STELLAR ;
printk ( KERN_INFO " %s stellar device found \n " , __func__ ) ;
break ;
default :
2008-06-14 18:27:18 -03:00
switch ( dev - > udev - > descriptor . idProduct ) {
case USB_PID_NOVA_A :
2008-06-14 00:43:26 -03:00
params . device_type = SMS_NOVA_A0 ;
2008-06-14 07:40:41 -03:00
printk ( KERN_INFO " %s nova A0 found \n " , __func__ ) ;
2008-06-14 18:27:18 -03:00
break ;
default :
case USB_PID_NOVA_B :
2008-06-14 00:43:26 -03:00
params . device_type = SMS_NOVA_B0 ;
2008-06-14 07:40:41 -03:00
printk ( KERN_INFO " %s nova B0 found \n " , __func__ ) ;
2008-06-14 18:27:18 -03:00
break ;
case USB_PID_VEGA :
2008-06-14 00:43:26 -03:00
params . device_type = SMS_VEGA ;
2008-06-14 07:40:41 -03:00
printk ( KERN_INFO " %s Vega found \n " , __func__ ) ;
2008-06-14 00:43:26 -03:00
}
2008-05-19 18:56:13 -03:00
2008-06-14 00:43:26 -03:00
dev - > buffer_size = USB2_BUFFER_SIZE ;
2008-06-15 15:14:13 -03:00
dev - > response_alignment =
dev - > udev - > ep_in [ 1 ] - > desc . wMaxPacketSize -
sizeof ( SmsMsgHdr_ST ) ;
2008-05-19 18:56:13 -03:00
2008-06-14 00:43:26 -03:00
params . flags | = SMS_DEVICE_FAMILY2 ;
break ;
2008-05-19 18:56:13 -03:00
}
params . device = & dev - > udev - > dev ;
params . buffer_size = dev - > buffer_size ;
params . num_buffers = MAX_BUFFERS ;
params . sendrequest_handler = smsusb_sendrequest ;
params . context = dev ;
2008-06-15 15:14:13 -03:00
snprintf ( params . devpath , sizeof ( params . devpath ) ,
" usb \\ %d-%s " , dev - > udev - > bus - > busnum , dev - > udev - > devpath ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
/* register in smscore */
2008-05-19 18:56:13 -03:00
rc = smscore_register_device ( & params , & dev - > coredev ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s smscore_register_device(...) failed, "
" rc %d \n " , __func__ , rc ) ;
2008-05-19 18:56:13 -03:00
smsusb_term_device ( intf ) ;
return rc ;
}
// initialize urbs
2008-06-15 15:14:13 -03:00
for ( i = 0 ; i < MAX_URBS ; i + + ) {
2008-05-19 18:56:13 -03:00
dev - > surbs [ i ] . dev = dev ;
usb_init_urb ( & dev - > surbs [ i ] . urb ) ;
}
2008-06-14 00:43:26 -03:00
printk ( KERN_INFO " %s smsusb_start_streaming(...). \n " , __func__ ) ;
2008-05-19 18:56:13 -03:00
rc = smsusb_start_streaming ( dev ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s smsusb_start_streaming(...) failed \n " ,
__func__ ) ;
2008-05-19 18:56:13 -03:00
smsusb_term_device ( intf ) ;
return rc ;
}
rc = smscore_start_device ( dev - > coredev ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s smscore_start_device(...) failed \n " ,
__func__ ) ;
2008-05-19 18:56:13 -03:00
smsusb_term_device ( intf ) ;
return rc ;
}
2008-05-22 18:30:17 -03:00
printk ( KERN_INFO " %s device %p created \n " , __func__ , dev ) ;
2008-05-19 18:56:13 -03:00
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 ;
2008-06-14 00:43:26 -03:00
rc = usb_clear_halt ( udev , usb_rcvbulkpipe ( udev , 0x81 ) ) ;
rc = usb_clear_halt ( udev , usb_rcvbulkpipe ( udev , 0x02 ) ) ;
2008-06-15 15:14:13 -03:00
if ( intf - > num_altsetting > 0 ) {
2008-05-19 18:56:13 -03:00
rc = usb_set_interface ( udev , intf - > cur_altsetting - > desc . bInterfaceNumber , 0 ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
printk ( KERN_INFO " %s usb_set_interface failed, "
" rc %d \n " , __func__ , rc ) ;
2008-05-19 18:56:13 -03:00
return rc ;
}
}
2008-06-15 15:14:13 -03:00
printk ( KERN_INFO " smsusb_probe %d \n " ,
intf - > cur_altsetting - > desc . bInterfaceNumber ) ;
2008-05-19 18:56:13 -03:00
for ( i = 0 ; i < intf - > cur_altsetting - > desc . bNumEndpoints ; i + + )
2008-06-15 15:14:13 -03:00
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 ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
if ( ( udev - > actconfig - > desc . bNumInterfaces = = 2 ) & &
( intf - > cur_altsetting - > desc . bInterfaceNumber = = 0 ) ) {
2008-05-19 18:56:13 -03:00
printk ( KERN_INFO " rom interface 0 is not used \n " ) ;
return - ENODEV ;
}
2008-06-15 15:14:13 -03:00
if ( intf - > cur_altsetting - > desc . bInterfaceNumber = = 1 ) {
snprintf ( devpath , sizeof ( devpath ) , " usb \\ %d-%s " ,
udev - > bus - > busnum , udev - > devpath ) ;
2008-06-14 00:43:26 -03:00
printk ( KERN_INFO " stellar device was found. \n " ) ;
2008-05-19 18:56:13 -03:00
return smsusb1_load_firmware ( udev , smscore_registry_getmode ( devpath ) ) ;
}
2008-06-14 00:43:26 -03:00
rc = smsusb_init_device ( intf ) ;
2008-06-14 07:40:41 -03:00
printk ( KERN_INFO " %s rc %d \n " , __func__ , rc ) ;
2008-06-14 00:43:26 -03:00
return rc ;
2008-05-19 18:56:13 -03:00
}
void smsusb_disconnect ( struct usb_interface * intf )
{
smsusb_term_device ( intf ) ;
}
2008-05-22 18:07:39 -03:00
static struct usb_device_id smsusb_id_table [ ] = {
2008-06-14 18:27:18 -03:00
{ USB_DEVICE ( USB_VID_SIANO , 0x0010 ) } ,
{ USB_DEVICE ( USB_VID_SIANO , USB_PID_STELLAR ) } ,
{ USB_DEVICE ( USB_VID_SIANO , USB_PID_NOVA_A ) } ,
2008-05-22 18:07:39 -03:00
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , smsusb_id_table ) ;
2008-05-19 18:56:13 -03:00
static struct usb_driver smsusb_driver = {
. name = " smsusb " ,
. probe = smsusb_probe ,
2008-06-15 15:14:13 -03:00
. disconnect = smsusb_disconnect ,
2008-05-19 18:56:13 -03:00
. id_table = smsusb_id_table ,
} ;
2008-05-22 18:04:36 -03:00
int smsusb_register ( void )
2008-05-19 18:56:13 -03:00
{
int rc = usb_register ( & smsusb_driver ) ;
if ( rc )
printk ( KERN_INFO " usb_register failed. Error number %d \n " , rc ) ;
2008-05-22 18:30:17 -03:00
printk ( KERN_INFO " %s \n " , __func__ ) ;
2008-05-19 18:56:13 -03:00
return rc ;
}
2008-05-22 18:04:36 -03:00
void smsusb_unregister ( void )
2008-05-19 18:56:13 -03:00
{
2008-05-22 18:01:02 -03:00
/* Regular USB Cleanup */
2008-05-19 18:56:13 -03:00
usb_deregister ( & smsusb_driver ) ;
2008-05-22 18:30:17 -03:00
printk ( KERN_INFO " %s \n " , __func__ ) ;
2008-05-19 18:56:13 -03:00
}