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/module.h>
# include <linux/init.h>
# include "smscoreapi.h"
2008-06-18 22:09:55 -03:00
# include "sms-cards.h"
2008-05-19 18:56:13 -03:00
2008-05-19 18:57:12 -03:00
DVB_DEFINE_MOD_OPT_ADAPTER_NR ( adapter_nr ) ;
2008-05-19 18:56:13 -03:00
struct list_head g_smsdvb_clients ;
2008-06-29 15:15:19 -03:00
struct mutex g_smsdvb_clientslock ;
2008-05-19 18:56:13 -03:00
2008-06-21 02:44:02 -03:00
static int smsdvb_onresponse ( void * context , struct smscore_buffer_t * cb )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client = ( struct smsdvb_client_t * ) context ;
struct SmsMsgHdr_ST * phdr =
( struct SmsMsgHdr_ST * ) ( ( ( u8 * ) cb - > p ) + cb - > offset ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:52:43 -03:00
switch ( phdr - > msgType ) {
2008-06-15 15:14:13 -03:00
case MSG_SMS_DVBT_BDA_DATA :
dvb_dmx_swfilter ( & client - > demux , ( u8 * ) ( phdr + 1 ) ,
2008-06-15 17:52:24 -03:00
cb - > size - sizeof ( struct SmsMsgHdr_ST ) ) ;
2008-06-15 15:14:13 -03:00
break ;
case MSG_SMS_RF_TUNE_RES :
complete ( & client - > tune_done ) ;
break ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
case MSG_SMS_GET_STATISTICS_RES :
{
2008-06-15 17:52:24 -03:00
struct SmsMsgStatisticsInfo_ST * p =
( struct SmsMsgStatisticsInfo_ST * ) ( phdr + 1 ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:52:43 -03:00
if ( p - > Stat . IsDemodLocked ) {
2008-06-15 15:14:13 -03:00
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 ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:14:13 -03:00
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 ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 15:14:13 -03:00
complete ( & client - > stat_done ) ;
break ;
} }
2008-05-19 18:56:13 -03:00
smscore_putbuffer ( client - > coredev , cb ) ;
return 0 ;
}
2008-06-21 02:44:02 -03:00
static void smsdvb_unregister_client ( struct smsdvb_client_t * client )
2008-05-19 18:56:13 -03:00
{
2008-06-15 15:52:43 -03:00
/* must be called under clientslock */
2008-05-19 18:56:13 -03:00
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 ) ;
}
2008-06-21 02:44:02 -03:00
static void smsdvb_onremove ( void * context )
2008-05-19 18:56:13 -03:00
{
kmutex_lock ( & g_smsdvb_clientslock ) ;
2008-06-15 17:52:24 -03:00
smsdvb_unregister_client ( ( struct smsdvb_client_t * ) context ) ;
2008-05-19 18:56:13 -03:00
kmutex_unlock ( & g_smsdvb_clientslock ) ;
}
static int smsdvb_start_feed ( struct dvb_demux_feed * feed )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( feed - > demux , struct smsdvb_client_t , demux ) ;
struct SmsMsgData_ST PidMsg ;
2008-05-19 18:56:13 -03:00
2008-06-19 20:35:21 -03:00
sms_debug ( " add pid %d(%x) " ,
2008-06-19 01:15:46 -03:00
feed - > pid , feed - > pid ) ;
2008-05-19 18:56:13 -03:00
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 ;
2008-06-15 15:14:13 -03:00
return smsclient_sendrequest ( client - > smsclient ,
& PidMsg , sizeof ( PidMsg ) ) ;
2008-05-19 18:56:13 -03:00
}
static int smsdvb_stop_feed ( struct dvb_demux_feed * feed )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( feed - > demux , struct smsdvb_client_t , demux ) ;
struct SmsMsgData_ST PidMsg ;
2008-05-19 18:56:13 -03:00
2008-06-19 20:35:21 -03:00
sms_debug ( " remove pid %d(%x) " ,
2008-06-19 01:15:46 -03:00
feed - > pid , feed - > pid ) ;
2008-05-19 18:56:13 -03:00
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 ;
2008-06-15 15:14:13 -03:00
return smsclient_sendrequest ( client - > smsclient ,
& PidMsg , sizeof ( PidMsg ) ) ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 17:52:24 -03:00
static int smsdvb_sendrequest_and_wait ( struct smsdvb_client_t * client ,
2008-05-06 03:11:51 -03:00
void * buffer , size_t size ,
struct completion * completion )
2008-05-19 18:56:13 -03:00
{
int rc = smsclient_sendrequest ( client - > smsclient , buffer , size ) ;
if ( rc < 0 )
return rc ;
2008-06-15 15:14:13 -03:00
return wait_for_completion_timeout ( completion ,
msecs_to_jiffies ( 2000 ) ) ?
0 : - ETIME ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 17:52:24 -03:00
static int smsdvb_send_statistics_request ( struct smsdvb_client_t * client )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ ,
2008-06-15 15:14:13 -03:00
DVBT_BDA_CONTROL_MSG_ID ,
2008-06-15 17:52:24 -03:00
HIF_TASK , sizeof ( struct SmsMsgHdr_ST ) , 0 } ;
2008-06-15 15:14:13 -03:00
return smsdvb_sendrequest_and_wait ( client , & Msg , sizeof ( Msg ) ,
& client - > stat_done ) ;
2008-05-19 18:56:13 -03:00
}
static int smsdvb_read_status ( struct dvb_frontend * fe , fe_status_t * stat )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
2008-05-19 18:56:13 -03:00
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 )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
2008-05-19 18:56:13 -03:00
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 )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
2008-05-19 18:56:13 -03:00
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 )
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
2008-05-19 18:56:13 -03:00
int rc = smsdvb_send_statistics_request ( client ) ;
if ( ! rc )
* snr = client - > fe_snr ;
return rc ;
}
2008-06-15 15:14:13 -03:00
static int smsdvb_get_tune_settings ( struct dvb_frontend * fe ,
struct dvb_frontend_tune_settings * tune )
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
tune - > min_delay_ms = 400 ;
tune - > step_size = 250000 ;
tune - > max_drift = 0 ;
return 0 ;
}
2008-06-15 15:14:13 -03:00
static int smsdvb_set_frontend ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * fep )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 17:52:24 -03:00
struct {
struct SmsMsgHdr_ST Msg ;
2008-06-15 15:14:13 -03:00
u32 Data [ 3 ] ;
2008-05-19 18:56:13 -03:00
} 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 ;
2008-06-19 20:35:21 -03:00
sms_debug ( " freq %d band %d " ,
2008-06-19 01:15:46 -03:00
fep - > frequency , fep - > u . ofdm . bandwidth ) ;
2008-05-19 18:56:13 -03:00
2008-06-15 15:52:43 -03:00
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_AUTO : return - EOPNOTSUPP ;
default : return - EINVAL ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 15:14:13 -03:00
return smsdvb_sendrequest_and_wait ( client , & Msg , sizeof ( Msg ) ,
& client - > tune_done ) ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 15:14:13 -03:00
static int smsdvb_get_frontend ( struct dvb_frontend * fe ,
struct dvb_frontend_parameters * fep )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsdvb_client_t * client =
container_of ( fe , struct smsdvb_client_t , frontend ) ;
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
2008-06-15 15:52:43 -03:00
/* todo: */
2008-06-15 15:14:13 -03:00
memcpy ( fep , & client - > fe_params ,
sizeof ( struct dvb_frontend_parameters ) ) ;
2008-05-19 18:56:13 -03:00
return 0 ;
}
static void smsdvb_release ( struct dvb_frontend * fe )
{
2008-06-15 15:52:43 -03:00
/* do nothing */
2008-05-19 18:56:13 -03:00
}
static struct dvb_frontend_ops smsdvb_fe_ops = {
. info = {
2008-06-18 22:09:55 -03:00
. name = " Siano Mobile Digital SMS1xxx " ,
2008-06-15 15:14:13 -03:00
. type = FE_OFDM ,
2008-05-19 18:56:13 -03:00
. frequency_min = 44250000 ,
. frequency_max = 867250000 ,
. frequency_stepsize = 250000 ,
. caps = FE_CAN_INVERSION_AUTO |
2008-06-15 15:14:13 -03:00
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 ,
2008-05-19 18:56:13 -03:00
} ,
. 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 ,
} ;
2008-06-21 02:44:02 -03:00
static int smsdvb_hotplug ( struct smscore_device_t * coredev ,
struct device * device , int arrival )
2008-05-19 18:56:13 -03:00
{
2008-06-15 17:52:24 -03:00
struct smsclient_params_t params ;
struct smsdvb_client_t * client ;
2008-05-19 18:56:13 -03:00
int rc ;
2008-06-15 15:52:43 -03:00
/* device removal handled by onremove callback */
2008-05-19 18:56:13 -03:00
if ( ! arrival )
return 0 ;
2008-06-15 15:14:13 -03:00
if ( smscore_get_device_mode ( coredev ) ! = 4 ) {
2008-06-19 20:35:21 -03:00
sms_err ( " SMS Device mode is not set for "
" DVB operation. " ) ;
2008-06-14 00:43:26 -03:00
return 0 ;
2008-05-19 18:56:13 -03:00
}
2008-06-15 17:52:24 -03:00
client = kzalloc ( sizeof ( struct smsdvb_client_t ) , GFP_KERNEL ) ;
2008-06-15 15:14:13 -03:00
if ( ! client ) {
2008-06-19 22:07:23 -03:00
sms_err ( " kmalloc() failed " ) ;
2008-05-19 18:56:13 -03:00
return - ENOMEM ;
}
2008-06-15 15:52:43 -03:00
/* register dvb adapter */
2008-06-18 22:09:55 -03:00
rc = dvb_register_adapter ( & client - > adapter ,
sms_get_board (
smscore_get_board_id ( coredev ) ) - > name ,
2008-06-15 15:14:13 -03:00
THIS_MODULE , device , adapter_nr ) ;
if ( rc < 0 ) {
2008-06-19 20:35:21 -03:00
sms_err ( " dvb_register_adapter() failed %d " , rc ) ;
2008-05-19 18:56:13 -03:00
goto adapter_error ;
}
2008-06-15 15:52:43 -03:00
/* init dvb demux */
2008-05-19 18:56:13 -03:00
client - > demux . dmx . capabilities = DMX_TS_FILTERING ;
2008-06-15 15:52:43 -03:00
client - > demux . filternum = 32 ; /* todo: nova ??? */
2008-05-19 18:56:13 -03:00
client - > demux . feednum = 32 ;
client - > demux . start_feed = smsdvb_start_feed ;
client - > demux . stop_feed = smsdvb_stop_feed ;
rc = dvb_dmx_init ( & client - > demux ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 20:35:21 -03:00
sms_err ( " dvb_dmx_init failed %d " , rc ) ;
2008-05-19 18:56:13 -03:00
goto dvbdmx_error ;
}
2008-06-15 15:52:43 -03:00
/* init dmxdev */
2008-05-19 18:56:13 -03:00
client - > dmxdev . filternum = 32 ;
client - > dmxdev . demux = & client - > demux . dmx ;
client - > dmxdev . capabilities = 0 ;
rc = dvb_dmxdev_init ( & client - > dmxdev , & client - > adapter ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 20:35:21 -03:00
sms_err ( " dvb_dmxdev_init failed %d " , rc ) ;
2008-05-19 18:56:13 -03:00
goto dmxdev_error ;
}
2008-06-15 15:52:43 -03:00
/* init and register frontend */
2008-06-15 15:14:13 -03:00
memcpy ( & client - > frontend . ops , & smsdvb_fe_ops ,
sizeof ( struct dvb_frontend_ops ) ) ;
2008-05-19 18:56:13 -03:00
rc = dvb_register_frontend ( & client - > adapter , & client - > frontend ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 20:35:21 -03:00
sms_err ( " frontend registration failed %d " , rc ) ;
2008-05-19 18:56:13 -03:00
goto frontend_error ;
}
2008-06-14 00:43:26 -03:00
params . initial_id = 1 ;
2008-05-19 18:56:13 -03:00
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 ) ;
2008-06-15 15:14:13 -03:00
if ( rc < 0 ) {
2008-06-19 22:07:23 -03:00
sms_err ( " smscore_register_client() failed %d " , rc ) ;
2008-05-19 18:56:13 -03:00
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 ) ;
2008-06-19 20:35:21 -03:00
sms_info ( " success " ) ;
2008-05-19 18:56:13 -03:00
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 ;
}
2008-05-22 18:04:36 -03:00
int smsdvb_register ( void )
{
int rc ;
INIT_LIST_HEAD ( & g_smsdvb_clients ) ;
kmutex_init ( & g_smsdvb_clientslock ) ;
rc = smscore_register_hotplug ( smsdvb_hotplug ) ;
2008-06-19 20:35:21 -03:00
sms_debug ( " " ) ;
2008-05-22 18:04:36 -03:00
return rc ;
}
void smsdvb_unregister ( void )
{
smscore_unregister_hotplug ( smsdvb_hotplug ) ;
kmutex_lock ( & g_smsdvb_clientslock ) ;
while ( ! list_empty ( & g_smsdvb_clients ) )
2008-06-15 15:14:13 -03:00
smsdvb_unregister_client (
2008-06-15 17:52:24 -03:00
( struct smsdvb_client_t * ) g_smsdvb_clients . next ) ;
2008-05-22 18:04:36 -03:00
kmutex_unlock ( & g_smsdvb_clientslock ) ;
}