2008-05-22 18:05:26 -03:00
/*
2008-06-28 17:09:28 -03:00
* Driver for the Siano SMS1xxx USB dongle
2008-05-22 18:05:26 -03:00
*
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"
2008-06-18 22:09:55 -03:00
# include "sms-cards.h"
2008-05-19 18:56:13 -03:00
# define USB1_BUFFER_SIZE 0x1000
# define USB2_BUFFER_SIZE 0x4000
# define MAX_BUFFERS 50
# define MAX_URBS 10
2008-06-15 17:52:24 -03:00
struct smsusb_device_t ;
2008-05-19 18:56:13 -03:00
2008-06-15 17:52:24 -03:00
struct smsusb_urb_t {
struct smscore_buffer_t * cb ;
struct smsusb_device_t * dev ;
2008-05-19 18:56:13 -03:00
2008-06-15 17:52:24 -03:00
struct urb urb ;
} ;
2008-05-19 18:56:13 -03:00
2008-06-15 17:52:24 -03:00
struct smsusb_device_t {
2008-05-06 03:11:51 -03:00
struct usb_device * udev ;
2008-06-15 17:52:24 -03:00
struct smscore_device_t * coredev ;
2008-05-19 18:56:13 -03:00
2008-06-15 17:52:24 -03:00
struct smsusb_urb_t surbs [ MAX_URBS ] ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
int response_alignment ;
int buffer_size ;
2008-06-15 17:52:24 -03:00
} ;
2008-05-19 18:56:13 -03:00
2008-06-21 02:44:02 -03:00
static int smsusb_submit_urb ( struct smsusb_device_t * dev ,
struct smsusb_urb_t * surb ) ;
2008-05-19 18:56:13 -03:00
2008-06-21 02:44:02 -03:00
static void smsusb_onresponse ( struct urb * urb )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsusb_urb_t * surb = ( struct smsusb_urb_t * ) urb - > context ;
struct smsusb_device_t * dev = surb - > dev ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
if ( urb - > status < 0 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " error, urb status %d, %d bytes " ,
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-06-15 17:52:24 -03:00
struct SmsMsgHdr_ST * phdr = ( struct SmsMsgHdr_ST * ) surb - > cb - > p ;
2008-05-19 18:56:13 -03:00
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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " invalid response "
" msglen %d offset %d "
" size %d " ,
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 ,
2008-06-15 17:52:24 -03:00
phdr , sizeof ( struct 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 {
2008-06-19 22:07:23 -03:00
sms_err ( " invalid response "
" msglen %d actual %d " ,
phdr - > msgLength , urb - > actual_length ) ;
2008-05-19 18:56:13 -03:00
}
}
exit_and_resubmit :
smsusb_submit_urb ( dev , surb ) ;
}
2008-06-21 02:44:02 -03:00
static int smsusb_submit_urb ( struct smsusb_device_t * dev ,
struct 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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smscore_getbuffer(...) returned NULL " ) ;
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-06-21 02:44:02 -03:00
static void smsusb_stop_streaming ( struct smsusb_device_t * dev )
2008-05-19 18:56:13 -03:00
{
int i ;
2008-06-15 15:52:43 -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-06-21 02:44:02 -03:00
static int smsusb_start_streaming ( struct smsusb_device_t * dev )
2008-05-19 18:56:13 -03:00
{
int i , rc ;
2008-06-15 15:52:43 -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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smsusb_submit_urb(...) failed " ) ;
2008-05-19 18:56:13 -03:00
smsusb_stop_streaming ( dev ) ;
break ;
}
}
return rc ;
}
2008-06-21 02:44:02 -03:00
static int smsusb_sendrequest ( void * context , void * buffer , size_t size )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsusb_device_t * dev = ( struct 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-21 02:44:02 -03:00
static 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 " ,
} ;
2008-06-21 02:44:02 -03:00
static int smsusb1_load_firmware ( struct usb_device * udev , int id )
2008-05-19 18:56:13 -03:00
{
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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " invalid firmware id specified %d " , 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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " failed to open \" %s \" mode %d " ,
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-19 20:35:21 -03:00
sms_info ( " sent %d(%d) bytes, rc %d " , 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-06-19 22:07:23 -03:00
sms_err ( " failed to allocate firmware buffer " ) ;
2008-05-19 18:56:13 -03:00
rc = - ENOMEM ;
}
2008-06-26 04:58:30 -03:00
sms_info ( " read FW %s, size=%d " , smsusb1_fw_lkup [ id ] , fw - > size ) ;
2008-05-19 18:56:13 -03:00
release_firmware ( fw ) ;
return rc ;
}
2008-06-21 02:44:02 -03:00
static void smsusb1_detectmode ( void * context , int * mode )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
char * product_string =
( ( struct smsusb_device_t * ) context ) - > udev - > product ;
2008-05-19 18:56:13 -03:00
* 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-06-19 20:35:21 -03:00
sms_err ( " product string not found " ) ;
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-06-19 20:35:21 -03:00
sms_info ( " %d \" %s \" " , * mode , product_string ) ;
2008-05-19 18:56:13 -03:00
}
2008-06-21 02:44:02 -03:00
static int smsusb1_setmode ( void * context , int mode )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ , 0 , HIF_TASK ,
sizeof ( struct 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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " invalid firmware id specified %d " , mode ) ;
2008-05-19 18:56:13 -03:00
return - EINVAL ;
}
return smsusb_sendrequest ( context , & Msg , sizeof ( Msg ) ) ;
}
2008-06-21 02:44:02 -03:00
static void smsusb_term_device ( struct usb_interface * intf )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsusb_device_t * dev =
( struct 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 ) ;
2008-06-15 15:52:43 -03:00
/* unregister from smscore */
2008-05-19 18:56:13 -03:00
if ( dev - > coredev )
smscore_unregister_device ( dev - > coredev ) ;
kfree ( dev ) ;
2008-06-19 20:35:21 -03:00
sms_info ( " device %p destroyed " , dev ) ;
2008-05-19 18:56:13 -03:00
}
usb_set_intfdata ( intf , NULL ) ;
}
2008-06-21 02:44:02 -03:00
static int smsusb_init_device ( struct usb_interface * intf , int board_id )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsdevice_params_t params ;
struct smsusb_device_t * dev ;
2008-05-19 18:56:13 -03:00
int i , rc ;
2008-06-15 15:52:43 -03:00
/* create device object */
2008-06-15 17:52:24 -03:00
dev = kzalloc ( sizeof ( struct smsusb_device_t ) , GFP_KERNEL ) ;
2008-06-15 15:14:13 -03:00
if ( ! dev ) {
2008-06-19 22:07:23 -03:00
sms_err ( " kzalloc(sizeof(struct smsusb_device_t) failed " ) ;
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-21 05:24:38 -03:00
params . device_type = sms_get_board ( board_id ) - > type ;
2008-06-14 18:27:18 -03:00
2008-06-21 05:24:38 -03:00
switch ( params . device_type ) {
2008-06-18 22:09:55 -03:00
case SMS_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 ;
break ;
default :
2008-06-21 05:24:38 -03:00
sms_err ( " Unspecified sms device type! " ) ;
/* fall-thru */
case SMS_NOVA_A0 :
case SMS_NOVA_B0 :
case SMS_VEGA :
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 -
2008-06-15 17:52:24 -03:00
sizeof ( struct 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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smscore_register_device(...) failed, rc %d " , rc ) ;
2008-05-19 18:56:13 -03:00
smsusb_term_device ( intf ) ;
return rc ;
}
2008-06-18 22:09:55 -03:00
smscore_set_board_id ( dev - > coredev , board_id ) ;
2008-06-15 15:52:43 -03:00
/* 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-19 20:35:21 -03:00
sms_info ( " smsusb_start_streaming(...). " ) ;
2008-05-19 18:56:13 -03:00
rc = smsusb_start_streaming ( dev ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smsusb_start_streaming(...) failed " ) ;
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 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smscore_start_device(...) failed " ) ;
2008-05-19 18:56:13 -03:00
smsusb_term_device ( intf ) ;
return rc ;
}
2008-06-19 20:35:21 -03:00
sms_info ( " device %p created " , dev ) ;
2008-05-19 18:56:13 -03:00
return rc ;
}
2008-06-21 02:44:02 -03:00
static int smsusb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
2008-05-19 18:56:13 -03:00
{
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-06-15 16:50:11 -03:00
rc = usb_set_interface (
udev , intf - > cur_altsetting - > desc . bInterfaceNumber , 0 ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " usb_set_interface failed, rc %d " , rc ) ;
2008-05-19 18:56:13 -03:00
return rc ;
}
}
2008-06-19 20:35:21 -03:00
sms_info ( " smsusb_probe %d " ,
2008-06-15 15:14:13 -03:00
intf - > cur_altsetting - > desc . bInterfaceNumber ) ;
2008-06-15 15:52:43 -03:00
for ( i = 0 ; i < intf - > cur_altsetting - > desc . bNumEndpoints ; i + + )
2008-06-19 20:35:21 -03:00
sms_info ( " endpoint %d %02x %02x %d " , i ,
2008-06-15 15:14:13 -03:00
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-06-19 22:07:23 -03:00
sms_err ( " rom interface 0 is not used " ) ;
2008-05-19 18:56:13 -03:00
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-19 20:35:21 -03:00
sms_info ( " stellar device was found. " ) ;
2008-06-15 16:50:11 -03:00
return smsusb1_load_firmware (
udev , smscore_registry_getmode ( devpath ) ) ;
2008-05-19 18:56:13 -03:00
}
2008-06-18 22:09:55 -03:00
rc = smsusb_init_device ( intf , id - > driver_info ) ;
2008-06-19 20:35:21 -03:00
sms_info ( " rc %d " , rc ) ;
2008-06-14 00:43:26 -03:00
return rc ;
2008-05-19 18:56:13 -03:00
}
2008-06-21 02:44:02 -03:00
static void smsusb_disconnect ( struct usb_interface * intf )
2008-05-19 18:56:13 -03:00
{
smsusb_term_device ( intf ) ;
}
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 )
2008-06-19 22:07:23 -03:00
sms_err ( " usb_register failed. Error number %d " , rc ) ;
2008-05-19 18:56:13 -03:00
2008-06-19 20:35:21 -03:00
sms_debug ( " " ) ;
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-06-19 20:35:21 -03:00
sms_debug ( " " ) ;
2008-05-22 18:01:02 -03:00
/* Regular USB Cleanup */
2008-05-19 18:56:13 -03:00
usb_deregister ( & smsusb_driver ) ;
}