2 * Driver for the Siano SMS1xxx USB dongle
4 * author: Anatoly Greenblat
6 * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation;
12 * Software distributed under the License is distributed on an "AS IS"
13 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
15 * See the 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, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <linux/module.h>
23 #include <linux/init.h>
25 #include "smscoreapi.h"
26 #include "sms-cards.h"
28 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr
);
30 static struct list_head g_smsdvb_clients
;
31 static struct mutex g_smsdvb_clientslock
;
33 static int smsdvb_onresponse(void *context
, struct smscore_buffer_t
*cb
)
35 struct smsdvb_client_t
*client
= (struct smsdvb_client_t
*) context
;
36 struct SmsMsgHdr_ST
*phdr
=
37 (struct SmsMsgHdr_ST
*)(((u8
*) cb
->p
) + cb
->offset
);
39 switch (phdr
->msgType
) {
40 case MSG_SMS_DVBT_BDA_DATA
:
41 dvb_dmx_swfilter(&client
->demux
, (u8
*)(phdr
+ 1),
42 cb
->size
- sizeof(struct SmsMsgHdr_ST
));
45 case MSG_SMS_RF_TUNE_RES
:
46 complete(&client
->tune_done
);
49 case MSG_SMS_GET_STATISTICS_RES
:
51 struct SmsMsgStatisticsInfo_ST
*p
=
52 (struct SmsMsgStatisticsInfo_ST
*)(phdr
+ 1);
54 if (p
->Stat
.IsDemodLocked
) {
55 client
->fe_status
= FE_HAS_SIGNAL
|
61 client
->fe_snr
= p
->Stat
.SNR
;
62 client
->fe_ber
= p
->Stat
.BER
;
63 client
->fe_unc
= p
->Stat
.BERErrorCount
;
65 if (p
->Stat
.InBandPwr
< -95)
66 client
->fe_signal_strength
= 0;
67 else if (p
->Stat
.InBandPwr
> -29)
68 client
->fe_signal_strength
= 100;
70 client
->fe_signal_strength
=
71 (p
->Stat
.InBandPwr
+ 95) * 3 / 2;
73 client
->fe_status
= 0;
77 client
->fe_signal_strength
= 0;
80 complete(&client
->stat_done
);
84 smscore_putbuffer(client
->coredev
, cb
);
89 static void smsdvb_unregister_client(struct smsdvb_client_t
*client
)
91 /* must be called under clientslock */
93 list_del(&client
->entry
);
95 smscore_unregister_client(client
->smsclient
);
96 dvb_unregister_frontend(&client
->frontend
);
97 dvb_dmxdev_release(&client
->dmxdev
);
98 dvb_dmx_release(&client
->demux
);
99 dvb_unregister_adapter(&client
->adapter
);
103 static void smsdvb_onremove(void *context
)
105 kmutex_lock(&g_smsdvb_clientslock
);
107 smsdvb_unregister_client((struct smsdvb_client_t
*) context
);
109 kmutex_unlock(&g_smsdvb_clientslock
);
112 static int smsdvb_start_feed(struct dvb_demux_feed
*feed
)
114 struct smsdvb_client_t
*client
=
115 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
116 struct SmsMsgData_ST PidMsg
;
118 sms_debug("add pid %d(%x)",
119 feed
->pid
, feed
->pid
);
121 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
122 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
123 PidMsg
.xMsgHeader
.msgFlags
= 0;
124 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_ADD_PID_FILTER_REQ
;
125 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
126 PidMsg
.msgData
[0] = feed
->pid
;
128 return smsclient_sendrequest(client
->smsclient
,
129 &PidMsg
, sizeof(PidMsg
));
132 static int smsdvb_stop_feed(struct dvb_demux_feed
*feed
)
134 struct smsdvb_client_t
*client
=
135 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
136 struct SmsMsgData_ST PidMsg
;
138 sms_debug("remove pid %d(%x)",
139 feed
->pid
, feed
->pid
);
141 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
142 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
143 PidMsg
.xMsgHeader
.msgFlags
= 0;
144 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_REMOVE_PID_FILTER_REQ
;
145 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
146 PidMsg
.msgData
[0] = feed
->pid
;
148 return smsclient_sendrequest(client
->smsclient
,
149 &PidMsg
, sizeof(PidMsg
));
152 static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t
*client
,
153 void *buffer
, size_t size
,
154 struct completion
*completion
)
156 int rc
= smsclient_sendrequest(client
->smsclient
, buffer
, size
);
160 return wait_for_completion_timeout(completion
,
161 msecs_to_jiffies(2000)) ?
165 static int smsdvb_send_statistics_request(struct smsdvb_client_t
*client
)
167 struct SmsMsgHdr_ST Msg
= { MSG_SMS_GET_STATISTICS_REQ
,
168 DVBT_BDA_CONTROL_MSG_ID
,
169 HIF_TASK
, sizeof(struct SmsMsgHdr_ST
), 0 };
170 int ret
= smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
175 if (client
->fe_status
& FE_HAS_LOCK
)
176 sms_board_led_feedback(client
->coredev
,
177 (client
->fe_unc
== 0) ?
178 SMS_LED_HI
: SMS_LED_LO
);
180 sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
184 static int smsdvb_read_status(struct dvb_frontend
*fe
, fe_status_t
*stat
)
186 struct smsdvb_client_t
*client
=
187 container_of(fe
, struct smsdvb_client_t
, frontend
);
188 int rc
= smsdvb_send_statistics_request(client
);
191 *stat
= client
->fe_status
;
196 static int smsdvb_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
198 struct smsdvb_client_t
*client
=
199 container_of(fe
, struct smsdvb_client_t
, frontend
);
200 int rc
= smsdvb_send_statistics_request(client
);
203 *ber
= client
->fe_ber
;
208 static int smsdvb_read_signal_strength(struct dvb_frontend
*fe
, u16
*strength
)
210 struct smsdvb_client_t
*client
=
211 container_of(fe
, struct smsdvb_client_t
, frontend
);
212 int rc
= smsdvb_send_statistics_request(client
);
215 *strength
= client
->fe_signal_strength
;
220 static int smsdvb_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
222 struct smsdvb_client_t
*client
=
223 container_of(fe
, struct smsdvb_client_t
, frontend
);
224 int rc
= smsdvb_send_statistics_request(client
);
227 *snr
= client
->fe_snr
;
232 static int smsdvb_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
234 struct smsdvb_client_t
*client
=
235 container_of(fe
, struct smsdvb_client_t
, frontend
);
236 int rc
= smsdvb_send_statistics_request(client
);
239 *ucblocks
= client
->fe_unc
;
244 static int smsdvb_get_tune_settings(struct dvb_frontend
*fe
,
245 struct dvb_frontend_tune_settings
*tune
)
249 tune
->min_delay_ms
= 400;
250 tune
->step_size
= 250000;
255 static int smsdvb_set_frontend(struct dvb_frontend
*fe
,
256 struct dvb_frontend_parameters
*fep
)
258 struct smsdvb_client_t
*client
=
259 container_of(fe
, struct smsdvb_client_t
, frontend
);
262 struct SmsMsgHdr_ST Msg
;
266 Msg
.Msg
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
267 Msg
.Msg
.msgDstId
= HIF_TASK
;
268 Msg
.Msg
.msgFlags
= 0;
269 Msg
.Msg
.msgType
= MSG_SMS_RF_TUNE_REQ
;
270 Msg
.Msg
.msgLength
= sizeof(Msg
);
271 Msg
.Data
[0] = fep
->frequency
;
272 Msg
.Data
[2] = 12000000;
274 sms_debug("freq %d band %d",
275 fep
->frequency
, fep
->u
.ofdm
.bandwidth
);
277 switch (fep
->u
.ofdm
.bandwidth
) {
278 case BANDWIDTH_8_MHZ
: Msg
.Data
[1] = BW_8_MHZ
; break;
279 case BANDWIDTH_7_MHZ
: Msg
.Data
[1] = BW_7_MHZ
; break;
280 case BANDWIDTH_6_MHZ
: Msg
.Data
[1] = BW_6_MHZ
; break;
281 case BANDWIDTH_AUTO
: return -EOPNOTSUPP
;
282 default: return -EINVAL
;
285 return smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
289 static int smsdvb_get_frontend(struct dvb_frontend
*fe
,
290 struct dvb_frontend_parameters
*fep
)
292 struct smsdvb_client_t
*client
=
293 container_of(fe
, struct smsdvb_client_t
, frontend
);
298 memcpy(fep
, &client
->fe_params
,
299 sizeof(struct dvb_frontend_parameters
));
304 static int smsdvb_init(struct dvb_frontend
*fe
)
306 struct smsdvb_client_t
*client
=
307 container_of(fe
, struct smsdvb_client_t
, frontend
);
309 sms_board_power(client
->coredev
, 1);
314 static int smsdvb_sleep(struct dvb_frontend
*fe
)
316 struct smsdvb_client_t
*client
=
317 container_of(fe
, struct smsdvb_client_t
, frontend
);
319 sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
320 sms_board_power(client
->coredev
, 0);
325 static void smsdvb_release(struct dvb_frontend
*fe
)
330 static struct dvb_frontend_ops smsdvb_fe_ops
= {
332 .name
= "Siano Mobile Digital SMS1xxx",
334 .frequency_min
= 44250000,
335 .frequency_max
= 867250000,
336 .frequency_stepsize
= 250000,
337 .caps
= FE_CAN_INVERSION_AUTO
|
338 FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
339 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
340 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
341 FE_CAN_QAM_AUTO
| FE_CAN_TRANSMISSION_MODE_AUTO
|
342 FE_CAN_GUARD_INTERVAL_AUTO
|
344 FE_CAN_HIERARCHY_AUTO
,
347 .release
= smsdvb_release
,
349 .set_frontend
= smsdvb_set_frontend
,
350 .get_frontend
= smsdvb_get_frontend
,
351 .get_tune_settings
= smsdvb_get_tune_settings
,
353 .read_status
= smsdvb_read_status
,
354 .read_ber
= smsdvb_read_ber
,
355 .read_signal_strength
= smsdvb_read_signal_strength
,
356 .read_snr
= smsdvb_read_snr
,
357 .read_ucblocks
= smsdvb_read_ucblocks
,
360 .sleep
= smsdvb_sleep
,
363 static int smsdvb_hotplug(struct smscore_device_t
*coredev
,
364 struct device
*device
, int arrival
)
366 struct smsclient_params_t params
;
367 struct smsdvb_client_t
*client
;
370 /* device removal handled by onremove callback */
374 if (smscore_get_device_mode(coredev
) != 4) {
375 sms_err("SMS Device mode is not set for "
380 client
= kzalloc(sizeof(struct smsdvb_client_t
), GFP_KERNEL
);
382 sms_err("kmalloc() failed");
386 /* register dvb adapter */
387 rc
= dvb_register_adapter(&client
->adapter
,
389 smscore_get_board_id(coredev
))->name
,
390 THIS_MODULE
, device
, adapter_nr
);
392 sms_err("dvb_register_adapter() failed %d", rc
);
397 client
->demux
.dmx
.capabilities
= DMX_TS_FILTERING
;
398 client
->demux
.filternum
= 32; /* todo: nova ??? */
399 client
->demux
.feednum
= 32;
400 client
->demux
.start_feed
= smsdvb_start_feed
;
401 client
->demux
.stop_feed
= smsdvb_stop_feed
;
403 rc
= dvb_dmx_init(&client
->demux
);
405 sms_err("dvb_dmx_init failed %d", rc
);
410 client
->dmxdev
.filternum
= 32;
411 client
->dmxdev
.demux
= &client
->demux
.dmx
;
412 client
->dmxdev
.capabilities
= 0;
414 rc
= dvb_dmxdev_init(&client
->dmxdev
, &client
->adapter
);
416 sms_err("dvb_dmxdev_init failed %d", rc
);
420 /* init and register frontend */
421 memcpy(&client
->frontend
.ops
, &smsdvb_fe_ops
,
422 sizeof(struct dvb_frontend_ops
));
424 rc
= dvb_register_frontend(&client
->adapter
, &client
->frontend
);
426 sms_err("frontend registration failed %d", rc
);
430 params
.initial_id
= 1;
431 params
.data_type
= MSG_SMS_DVBT_BDA_DATA
;
432 params
.onresponse_handler
= smsdvb_onresponse
;
433 params
.onremove_handler
= smsdvb_onremove
;
434 params
.context
= client
;
436 rc
= smscore_register_client(coredev
, ¶ms
, &client
->smsclient
);
438 sms_err("smscore_register_client() failed %d", rc
);
442 client
->coredev
= coredev
;
444 init_completion(&client
->tune_done
);
445 init_completion(&client
->stat_done
);
447 kmutex_lock(&g_smsdvb_clientslock
);
449 list_add(&client
->entry
, &g_smsdvb_clients
);
451 kmutex_unlock(&g_smsdvb_clientslock
);
455 sms_board_setup(coredev
);
460 dvb_unregister_frontend(&client
->frontend
);
463 dvb_dmxdev_release(&client
->dmxdev
);
466 dvb_dmx_release(&client
->demux
);
469 dvb_unregister_adapter(&client
->adapter
);
476 int smsdvb_register(void)
480 INIT_LIST_HEAD(&g_smsdvb_clients
);
481 kmutex_init(&g_smsdvb_clientslock
);
483 rc
= smscore_register_hotplug(smsdvb_hotplug
);
490 void smsdvb_unregister(void)
492 smscore_unregister_hotplug(smsdvb_hotplug
);
494 kmutex_lock(&g_smsdvb_clientslock
);
496 while (!list_empty(&g_smsdvb_clients
))
497 smsdvb_unregister_client(
498 (struct smsdvb_client_t
*) g_smsdvb_clients
.next
);
500 kmutex_unlock(&g_smsdvb_clientslock
);