1 /****************************************************************
3 Siano Mobile Silicon, Inc.
4 MDTV receiver kernel modules.
5 Copyright (C) 2006-2008, Uri Shkolnik
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ****************************************************************/
22 #include <linux/module.h>
23 #include <linux/init.h>
27 #include "dvb_demux.h"
28 #include "dvb_frontend.h"
30 #include "smscoreapi.h"
31 #include "smsendian.h"
32 #include "sms-cards.h"
34 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr
);
36 struct smsdvb_client_t
{
37 struct list_head entry
;
39 struct smscore_device_t
*coredev
;
40 struct smscore_client_t
*smsclient
;
42 struct dvb_adapter adapter
;
43 struct dvb_demux demux
;
45 struct dvb_frontend frontend
;
47 fe_status_t fe_status
;
49 struct completion tune_done
;
51 /* todo: save freq/band instead whole struct */
52 struct dvb_frontend_parameters fe_params
;
54 struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb
;
59 static struct list_head g_smsdvb_clients
;
60 static struct mutex g_smsdvb_clientslock
;
63 module_param_named(debug
, sms_dbg
, int, 0644);
64 MODULE_PARM_DESC(debug
, "set debug level (info=1, adv=2 (or-able))");
66 /* Events that may come from DVB v3 adapter */
67 static void sms_board_dvb3_event(struct smsdvb_client_t
*client
,
68 enum SMS_DVB3_EVENTS event
) {
70 struct smscore_device_t
*coredev
= client
->coredev
;
73 sms_debug("DVB3_EVENT_INIT");
74 sms_board_event(coredev
, BOARD_EVENT_BIND
);
76 case DVB3_EVENT_SLEEP
:
77 sms_debug("DVB3_EVENT_SLEEP");
78 sms_board_event(coredev
, BOARD_EVENT_POWER_SUSPEND
);
80 case DVB3_EVENT_HOTPLUG
:
81 sms_debug("DVB3_EVENT_HOTPLUG");
82 sms_board_event(coredev
, BOARD_EVENT_POWER_INIT
);
84 case DVB3_EVENT_FE_LOCK
:
85 if (client
->event_fe_state
!= DVB3_EVENT_FE_LOCK
) {
86 client
->event_fe_state
= DVB3_EVENT_FE_LOCK
;
87 sms_debug("DVB3_EVENT_FE_LOCK");
88 sms_board_event(coredev
, BOARD_EVENT_FE_LOCK
);
91 case DVB3_EVENT_FE_UNLOCK
:
92 if (client
->event_fe_state
!= DVB3_EVENT_FE_UNLOCK
) {
93 client
->event_fe_state
= DVB3_EVENT_FE_UNLOCK
;
94 sms_debug("DVB3_EVENT_FE_UNLOCK");
95 sms_board_event(coredev
, BOARD_EVENT_FE_UNLOCK
);
98 case DVB3_EVENT_UNC_OK
:
99 if (client
->event_unc_state
!= DVB3_EVENT_UNC_OK
) {
100 client
->event_unc_state
= DVB3_EVENT_UNC_OK
;
101 sms_debug("DVB3_EVENT_UNC_OK");
102 sms_board_event(coredev
, BOARD_EVENT_MULTIPLEX_OK
);
105 case DVB3_EVENT_UNC_ERR
:
106 if (client
->event_unc_state
!= DVB3_EVENT_UNC_ERR
) {
107 client
->event_unc_state
= DVB3_EVENT_UNC_ERR
;
108 sms_debug("DVB3_EVENT_UNC_ERR");
109 sms_board_event(coredev
, BOARD_EVENT_MULTIPLEX_ERRORS
);
114 sms_err("Unknown dvb3 api event");
119 static int smsdvb_onresponse(void *context
, struct smscore_buffer_t
*cb
)
121 struct smsdvb_client_t
*client
= (struct smsdvb_client_t
*) context
;
122 struct SmsMsgHdr_ST
*phdr
= (struct SmsMsgHdr_ST
*) (((u8
*) cb
->p
)
124 u32
*pMsgData
= (u32
*) phdr
+ 1;
125 /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/
126 bool is_status_update
= false;
128 smsendian_handle_rx_message((struct SmsMsgData_ST
*) phdr
);
130 switch (phdr
->msgType
) {
131 case MSG_SMS_DVBT_BDA_DATA
:
132 dvb_dmx_swfilter(&client
->demux
, (u8
*)(phdr
+ 1),
133 cb
->size
- sizeof(struct SmsMsgHdr_ST
));
136 case MSG_SMS_RF_TUNE_RES
:
137 complete(&client
->tune_done
);
140 case MSG_SMS_SIGNAL_DETECTED_IND
:
141 sms_info("MSG_SMS_SIGNAL_DETECTED_IND");
142 client
->sms_stat_dvb
.TransmissionData
.IsDemodLocked
= true;
143 is_status_update
= true;
146 case MSG_SMS_NO_SIGNAL_IND
:
147 sms_info("MSG_SMS_NO_SIGNAL_IND");
148 client
->sms_stat_dvb
.TransmissionData
.IsDemodLocked
= false;
149 is_status_update
= true;
152 case MSG_SMS_TRANSMISSION_IND
: {
153 sms_info("MSG_SMS_TRANSMISSION_IND");
156 memcpy(&client
->sms_stat_dvb
.TransmissionData
, pMsgData
,
157 sizeof(struct TRANSMISSION_STATISTICS_S
));
159 /* Mo need to correct guard interval
160 * (as opposed to old statistics message).
162 CORRECT_STAT_BANDWIDTH(client
->sms_stat_dvb
.TransmissionData
);
163 CORRECT_STAT_TRANSMISSON_MODE(
164 client
->sms_stat_dvb
.TransmissionData
);
165 is_status_update
= true;
168 case MSG_SMS_HO_PER_SLICES_IND
: {
169 struct RECEPTION_STATISTICS_S
*pReceptionData
=
170 &client
->sms_stat_dvb
.ReceptionData
;
171 struct SRVM_SIGNAL_STATUS_S SignalStatusData
;
173 /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/
175 SignalStatusData
.result
= pMsgData
[0];
176 SignalStatusData
.snr
= pMsgData
[1];
177 SignalStatusData
.inBandPower
= (s32
) pMsgData
[2];
178 SignalStatusData
.tsPackets
= pMsgData
[3];
179 SignalStatusData
.etsPackets
= pMsgData
[4];
180 SignalStatusData
.constellation
= pMsgData
[5];
181 SignalStatusData
.hpCode
= pMsgData
[6];
182 SignalStatusData
.tpsSrvIndLP
= pMsgData
[7] & 0x03;
183 SignalStatusData
.tpsSrvIndHP
= pMsgData
[8] & 0x03;
184 SignalStatusData
.cellId
= pMsgData
[9] & 0xFFFF;
185 SignalStatusData
.reason
= pMsgData
[10];
186 SignalStatusData
.requestId
= pMsgData
[11];
187 pReceptionData
->IsRfLocked
= pMsgData
[16];
188 pReceptionData
->IsDemodLocked
= pMsgData
[17];
189 pReceptionData
->ModemState
= pMsgData
[12];
190 pReceptionData
->SNR
= pMsgData
[1];
191 pReceptionData
->BER
= pMsgData
[13];
192 pReceptionData
->RSSI
= pMsgData
[14];
193 CORRECT_STAT_RSSI(client
->sms_stat_dvb
.ReceptionData
);
195 pReceptionData
->InBandPwr
= (s32
) pMsgData
[2];
196 pReceptionData
->CarrierOffset
= (s32
) pMsgData
[15];
197 pReceptionData
->TotalTSPackets
= pMsgData
[3];
198 pReceptionData
->ErrorTSPackets
= pMsgData
[4];
201 if ((SignalStatusData
.tsPackets
+ SignalStatusData
.etsPackets
)
203 pReceptionData
->TS_PER
= (SignalStatusData
.etsPackets
204 * 100) / (SignalStatusData
.tsPackets
205 + SignalStatusData
.etsPackets
);
207 pReceptionData
->TS_PER
= 0;
210 pReceptionData
->BERBitCount
= pMsgData
[18];
211 pReceptionData
->BERErrorCount
= pMsgData
[19];
213 pReceptionData
->MRC_SNR
= pMsgData
[20];
214 pReceptionData
->MRC_InBandPwr
= pMsgData
[21];
215 pReceptionData
->MRC_RSSI
= pMsgData
[22];
217 is_status_update
= true;
221 smscore_putbuffer(client
->coredev
, cb
);
223 if (is_status_update
) {
224 if (client
->sms_stat_dvb
.ReceptionData
.IsDemodLocked
) {
225 client
->fe_status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
226 | FE_HAS_VITERBI
| FE_HAS_SYNC
| FE_HAS_LOCK
;
227 sms_board_dvb3_event(client
, DVB3_EVENT_FE_LOCK
);
228 if (client
->sms_stat_dvb
.ReceptionData
.ErrorTSPackets
230 sms_board_dvb3_event(client
, DVB3_EVENT_UNC_OK
);
232 sms_board_dvb3_event(client
,
236 /*client->fe_status =
237 (phdr->msgType == MSG_SMS_NO_SIGNAL_IND) ?
239 client
->fe_status
= 0;
240 sms_board_dvb3_event(client
, DVB3_EVENT_FE_UNLOCK
);
247 static void smsdvb_unregister_client(struct smsdvb_client_t
*client
)
249 /* must be called under clientslock */
251 list_del(&client
->entry
);
253 smscore_unregister_client(client
->smsclient
);
254 dvb_unregister_frontend(&client
->frontend
);
255 dvb_dmxdev_release(&client
->dmxdev
);
256 dvb_dmx_release(&client
->demux
);
257 dvb_unregister_adapter(&client
->adapter
);
261 static void smsdvb_onremove(void *context
)
263 kmutex_lock(&g_smsdvb_clientslock
);
265 smsdvb_unregister_client((struct smsdvb_client_t
*) context
);
267 kmutex_unlock(&g_smsdvb_clientslock
);
270 static int smsdvb_start_feed(struct dvb_demux_feed
*feed
)
272 struct smsdvb_client_t
*client
=
273 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
274 struct SmsMsgData_ST PidMsg
;
276 sms_debug("add pid %d(%x)",
277 feed
->pid
, feed
->pid
);
279 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
280 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
281 PidMsg
.xMsgHeader
.msgFlags
= 0;
282 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_ADD_PID_FILTER_REQ
;
283 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
284 PidMsg
.msgData
[0] = feed
->pid
;
286 smsendian_handle_tx_message((struct SmsMsgHdr_ST
*)&PidMsg
);
287 return smsclient_sendrequest(client
->smsclient
,
288 &PidMsg
, sizeof(PidMsg
));
291 static int smsdvb_stop_feed(struct dvb_demux_feed
*feed
)
293 struct smsdvb_client_t
*client
=
294 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
295 struct SmsMsgData_ST PidMsg
;
297 sms_debug("remove pid %d(%x)",
298 feed
->pid
, feed
->pid
);
300 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
301 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
302 PidMsg
.xMsgHeader
.msgFlags
= 0;
303 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_REMOVE_PID_FILTER_REQ
;
304 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
305 PidMsg
.msgData
[0] = feed
->pid
;
307 smsendian_handle_tx_message((struct SmsMsgHdr_ST
*)&PidMsg
);
308 return smsclient_sendrequest(client
->smsclient
,
309 &PidMsg
, sizeof(PidMsg
));
312 static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t
*client
,
313 void *buffer
, size_t size
,
314 struct completion
*completion
)
318 smsendian_handle_tx_message((struct SmsMsgHdr_ST
*)buffer
);
319 rc
= smsclient_sendrequest(client
->smsclient
, buffer
, size
);
323 return wait_for_completion_timeout(completion
,
324 msecs_to_jiffies(2000)) ?
328 static inline int led_feedback(struct smsdvb_client_t
*client
)
330 if (client
->fe_status
& FE_HAS_LOCK
)
331 return sms_board_led_feedback(client
->coredev
,
332 (client
->sms_stat_dvb
.ReceptionData
.BER
333 == 0) ? SMS_LED_HI
: SMS_LED_LO
);
335 return sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
338 static int smsdvb_read_status(struct dvb_frontend
*fe
, fe_status_t
*stat
)
340 struct smsdvb_client_t
*client
;
341 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
343 *stat
= client
->fe_status
;
345 led_feedback(client
);
350 static int smsdvb_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
352 struct smsdvb_client_t
*client
;
353 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
355 *ber
= client
->sms_stat_dvb
.ReceptionData
.BER
;
357 led_feedback(client
);
362 static int smsdvb_read_signal_strength(struct dvb_frontend
*fe
, u16
*strength
)
364 struct smsdvb_client_t
*client
;
365 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
367 if (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
< -95)
369 else if (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
> -29)
373 (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
376 led_feedback(client
);
381 static int smsdvb_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
383 struct smsdvb_client_t
*client
;
384 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
386 *snr
= client
->sms_stat_dvb
.ReceptionData
.SNR
;
388 led_feedback(client
);
393 static int smsdvb_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
395 struct smsdvb_client_t
*client
;
396 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
398 *ucblocks
= client
->sms_stat_dvb
.ReceptionData
.ErrorTSPackets
;
400 led_feedback(client
);
405 static int smsdvb_get_tune_settings(struct dvb_frontend
*fe
,
406 struct dvb_frontend_tune_settings
*tune
)
410 tune
->min_delay_ms
= 400;
411 tune
->step_size
= 250000;
416 static int smsdvb_set_frontend(struct dvb_frontend
*fe
,
417 struct dvb_frontend_parameters
*fep
)
419 struct smsdvb_client_t
*client
=
420 container_of(fe
, struct smsdvb_client_t
, frontend
);
423 struct SmsMsgHdr_ST Msg
;
429 client
->fe_status
= FE_HAS_SIGNAL
;
430 client
->event_fe_state
= -1;
431 client
->event_unc_state
= -1;
433 Msg
.Msg
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
434 Msg
.Msg
.msgDstId
= HIF_TASK
;
435 Msg
.Msg
.msgFlags
= 0;
436 Msg
.Msg
.msgType
= MSG_SMS_RF_TUNE_REQ
;
437 Msg
.Msg
.msgLength
= sizeof(Msg
);
438 Msg
.Data
[0] = fep
->frequency
;
439 Msg
.Data
[2] = 12000000;
441 sms_debug("freq %d band %d",
442 fep
->frequency
, fep
->u
.ofdm
.bandwidth
);
444 switch (fep
->u
.ofdm
.bandwidth
) {
445 case BANDWIDTH_8_MHZ
: Msg
.Data
[1] = BW_8_MHZ
; break;
446 case BANDWIDTH_7_MHZ
: Msg
.Data
[1] = BW_7_MHZ
; break;
447 case BANDWIDTH_6_MHZ
: Msg
.Data
[1] = BW_6_MHZ
; break;
448 case BANDWIDTH_AUTO
: return -EOPNOTSUPP
;
449 default: return -EINVAL
;
451 /* Disable LNA, if any. An error is returned if no LNA is present */
452 ret
= sms_board_lna_control(client
->coredev
, 0);
456 /* tune with LNA off at first */
457 ret
= smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
460 smsdvb_read_status(fe
, &status
);
462 if (status
& FE_HAS_LOCK
)
465 /* previous tune didnt lock - enable LNA and tune again */
466 sms_board_lna_control(client
->coredev
, 1);
469 return smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
473 static int smsdvb_get_frontend(struct dvb_frontend
*fe
,
474 struct dvb_frontend_parameters
*fep
)
476 struct smsdvb_client_t
*client
=
477 container_of(fe
, struct smsdvb_client_t
, frontend
);
482 memcpy(fep
, &client
->fe_params
,
483 sizeof(struct dvb_frontend_parameters
));
488 static int smsdvb_init(struct dvb_frontend
*fe
)
490 struct smsdvb_client_t
*client
=
491 container_of(fe
, struct smsdvb_client_t
, frontend
);
493 sms_board_power(client
->coredev
, 1);
495 sms_board_dvb3_event(client
, DVB3_EVENT_INIT
);
499 static int smsdvb_sleep(struct dvb_frontend
*fe
)
501 struct smsdvb_client_t
*client
=
502 container_of(fe
, struct smsdvb_client_t
, frontend
);
504 sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
505 sms_board_power(client
->coredev
, 0);
507 sms_board_dvb3_event(client
, DVB3_EVENT_SLEEP
);
512 static void smsdvb_release(struct dvb_frontend
*fe
)
517 static struct dvb_frontend_ops smsdvb_fe_ops
= {
519 .name
= "Siano Mobile Digital MDTV Receiver",
521 .frequency_min
= 44250000,
522 .frequency_max
= 867250000,
523 .frequency_stepsize
= 250000,
524 .caps
= FE_CAN_INVERSION_AUTO
|
525 FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
526 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
527 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
528 FE_CAN_QAM_AUTO
| FE_CAN_TRANSMISSION_MODE_AUTO
|
529 FE_CAN_GUARD_INTERVAL_AUTO
|
531 FE_CAN_HIERARCHY_AUTO
,
534 .release
= smsdvb_release
,
536 .set_frontend
= smsdvb_set_frontend
,
537 .get_frontend
= smsdvb_get_frontend
,
538 .get_tune_settings
= smsdvb_get_tune_settings
,
540 .read_status
= smsdvb_read_status
,
541 .read_ber
= smsdvb_read_ber
,
542 .read_signal_strength
= smsdvb_read_signal_strength
,
543 .read_snr
= smsdvb_read_snr
,
544 .read_ucblocks
= smsdvb_read_ucblocks
,
547 .sleep
= smsdvb_sleep
,
550 static int smsdvb_hotplug(struct smscore_device_t
*coredev
,
551 struct device
*device
, int arrival
)
553 struct smsclient_params_t params
;
554 struct smsdvb_client_t
*client
;
557 /* device removal handled by onremove callback */
561 if (smscore_get_device_mode(coredev
) != DEVICE_MODE_DVBT_BDA
) {
562 sms_err("SMS Device mode is not set for "
567 client
= kzalloc(sizeof(struct smsdvb_client_t
), GFP_KERNEL
);
569 sms_err("kmalloc() failed");
573 /* register dvb adapter */
574 rc
= dvb_register_adapter(&client
->adapter
,
576 smscore_get_board_id(coredev
))->name
,
577 THIS_MODULE
, device
, adapter_nr
);
579 sms_err("dvb_register_adapter() failed %d", rc
);
584 client
->demux
.dmx
.capabilities
= DMX_TS_FILTERING
;
585 client
->demux
.filternum
= 32; /* todo: nova ??? */
586 client
->demux
.feednum
= 32;
587 client
->demux
.start_feed
= smsdvb_start_feed
;
588 client
->demux
.stop_feed
= smsdvb_stop_feed
;
590 rc
= dvb_dmx_init(&client
->demux
);
592 sms_err("dvb_dmx_init failed %d", rc
);
597 client
->dmxdev
.filternum
= 32;
598 client
->dmxdev
.demux
= &client
->demux
.dmx
;
599 client
->dmxdev
.capabilities
= 0;
601 rc
= dvb_dmxdev_init(&client
->dmxdev
, &client
->adapter
);
603 sms_err("dvb_dmxdev_init failed %d", rc
);
607 /* init and register frontend */
608 memcpy(&client
->frontend
.ops
, &smsdvb_fe_ops
,
609 sizeof(struct dvb_frontend_ops
));
611 rc
= dvb_register_frontend(&client
->adapter
, &client
->frontend
);
613 sms_err("frontend registration failed %d", rc
);
617 params
.initial_id
= 1;
618 params
.data_type
= MSG_SMS_DVBT_BDA_DATA
;
619 params
.onresponse_handler
= smsdvb_onresponse
;
620 params
.onremove_handler
= smsdvb_onremove
;
621 params
.context
= client
;
623 rc
= smscore_register_client(coredev
, ¶ms
, &client
->smsclient
);
625 sms_err("smscore_register_client() failed %d", rc
);
629 client
->coredev
= coredev
;
631 init_completion(&client
->tune_done
);
633 kmutex_lock(&g_smsdvb_clientslock
);
635 list_add(&client
->entry
, &g_smsdvb_clients
);
637 kmutex_unlock(&g_smsdvb_clientslock
);
639 client
->event_fe_state
= -1;
640 client
->event_unc_state
= -1;
641 sms_board_dvb3_event(client
, DVB3_EVENT_HOTPLUG
);
644 sms_board_setup(coredev
);
649 dvb_unregister_frontend(&client
->frontend
);
652 dvb_dmxdev_release(&client
->dmxdev
);
655 dvb_dmx_release(&client
->demux
);
658 dvb_unregister_adapter(&client
->adapter
);
665 int smsdvb_module_init(void)
669 INIT_LIST_HEAD(&g_smsdvb_clients
);
670 kmutex_init(&g_smsdvb_clientslock
);
672 rc
= smscore_register_hotplug(smsdvb_hotplug
);
679 void smsdvb_module_exit(void)
681 smscore_unregister_hotplug(smsdvb_hotplug
);
683 kmutex_lock(&g_smsdvb_clientslock
);
685 while (!list_empty(&g_smsdvb_clients
))
686 smsdvb_unregister_client(
687 (struct smsdvb_client_t
*) g_smsdvb_clients
.next
);
689 kmutex_unlock(&g_smsdvb_clientslock
);
692 module_init(smsdvb_module_init
);
693 module_exit(smsdvb_module_exit
);
695 MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
696 MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
697 MODULE_LICENSE("GPL");