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/slab.h>
24 #include <linux/init.h>
28 #include "dvb_demux.h"
29 #include "dvb_frontend.h"
31 #include "smscoreapi.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
;
50 struct completion stats_done
;
52 struct SMSHOSTLIB_STATISTICS_DVB_EX_S sms_stat_dvb
;
57 static struct list_head g_smsdvb_clients
;
58 static struct mutex g_smsdvb_clientslock
;
61 module_param_named(debug
, sms_dbg
, int, 0644);
62 MODULE_PARM_DESC(debug
, "set debug level (info=1, adv=2 (or-able))");
64 /* Events that may come from DVB v3 adapter */
65 static void sms_board_dvb3_event(struct smsdvb_client_t
*client
,
66 enum SMS_DVB3_EVENTS event
) {
68 struct smscore_device_t
*coredev
= client
->coredev
;
71 sms_debug("DVB3_EVENT_INIT");
72 sms_board_event(coredev
, BOARD_EVENT_BIND
);
74 case DVB3_EVENT_SLEEP
:
75 sms_debug("DVB3_EVENT_SLEEP");
76 sms_board_event(coredev
, BOARD_EVENT_POWER_SUSPEND
);
78 case DVB3_EVENT_HOTPLUG
:
79 sms_debug("DVB3_EVENT_HOTPLUG");
80 sms_board_event(coredev
, BOARD_EVENT_POWER_INIT
);
82 case DVB3_EVENT_FE_LOCK
:
83 if (client
->event_fe_state
!= DVB3_EVENT_FE_LOCK
) {
84 client
->event_fe_state
= DVB3_EVENT_FE_LOCK
;
85 sms_debug("DVB3_EVENT_FE_LOCK");
86 sms_board_event(coredev
, BOARD_EVENT_FE_LOCK
);
89 case DVB3_EVENT_FE_UNLOCK
:
90 if (client
->event_fe_state
!= DVB3_EVENT_FE_UNLOCK
) {
91 client
->event_fe_state
= DVB3_EVENT_FE_UNLOCK
;
92 sms_debug("DVB3_EVENT_FE_UNLOCK");
93 sms_board_event(coredev
, BOARD_EVENT_FE_UNLOCK
);
96 case DVB3_EVENT_UNC_OK
:
97 if (client
->event_unc_state
!= DVB3_EVENT_UNC_OK
) {
98 client
->event_unc_state
= DVB3_EVENT_UNC_OK
;
99 sms_debug("DVB3_EVENT_UNC_OK");
100 sms_board_event(coredev
, BOARD_EVENT_MULTIPLEX_OK
);
103 case DVB3_EVENT_UNC_ERR
:
104 if (client
->event_unc_state
!= DVB3_EVENT_UNC_ERR
) {
105 client
->event_unc_state
= DVB3_EVENT_UNC_ERR
;
106 sms_debug("DVB3_EVENT_UNC_ERR");
107 sms_board_event(coredev
, BOARD_EVENT_MULTIPLEX_ERRORS
);
112 sms_err("Unknown dvb3 api event");
117 static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_EX_S
*pReceptionData
,
118 struct SMSHOSTLIB_STATISTICS_ST
*p
)
121 printk(KERN_DEBUG
"IsRfLocked = %d", p
->IsRfLocked
);
122 printk(KERN_DEBUG
"IsDemodLocked = %d", p
->IsDemodLocked
);
123 printk(KERN_DEBUG
"IsExternalLNAOn = %d", p
->IsExternalLNAOn
);
124 printk(KERN_DEBUG
"SNR = %d", p
->SNR
);
125 printk(KERN_DEBUG
"BER = %d", p
->BER
);
126 printk(KERN_DEBUG
"FIB_CRC = %d", p
->FIB_CRC
);
127 printk(KERN_DEBUG
"TS_PER = %d", p
->TS_PER
);
128 printk(KERN_DEBUG
"MFER = %d", p
->MFER
);
129 printk(KERN_DEBUG
"RSSI = %d", p
->RSSI
);
130 printk(KERN_DEBUG
"InBandPwr = %d", p
->InBandPwr
);
131 printk(KERN_DEBUG
"CarrierOffset = %d", p
->CarrierOffset
);
132 printk(KERN_DEBUG
"ModemState = %d", p
->ModemState
);
133 printk(KERN_DEBUG
"Frequency = %d", p
->Frequency
);
134 printk(KERN_DEBUG
"Bandwidth = %d", p
->Bandwidth
);
135 printk(KERN_DEBUG
"TransmissionMode = %d", p
->TransmissionMode
);
136 printk(KERN_DEBUG
"ModemState = %d", p
->ModemState
);
137 printk(KERN_DEBUG
"GuardInterval = %d", p
->GuardInterval
);
138 printk(KERN_DEBUG
"CodeRate = %d", p
->CodeRate
);
139 printk(KERN_DEBUG
"LPCodeRate = %d", p
->LPCodeRate
);
140 printk(KERN_DEBUG
"Hierarchy = %d", p
->Hierarchy
);
141 printk(KERN_DEBUG
"Constellation = %d", p
->Constellation
);
142 printk(KERN_DEBUG
"BurstSize = %d", p
->BurstSize
);
143 printk(KERN_DEBUG
"BurstDuration = %d", p
->BurstDuration
);
144 printk(KERN_DEBUG
"BurstCycleTime = %d", p
->BurstCycleTime
);
145 printk(KERN_DEBUG
"CalculatedBurstCycleTime = %d", p
->CalculatedBurstCycleTime
);
146 printk(KERN_DEBUG
"NumOfRows = %d", p
->NumOfRows
);
147 printk(KERN_DEBUG
"NumOfPaddCols = %d", p
->NumOfPaddCols
);
148 printk(KERN_DEBUG
"NumOfPunctCols = %d", p
->NumOfPunctCols
);
149 printk(KERN_DEBUG
"ErrorTSPackets = %d", p
->ErrorTSPackets
);
150 printk(KERN_DEBUG
"TotalTSPackets = %d", p
->TotalTSPackets
);
151 printk(KERN_DEBUG
"NumOfValidMpeTlbs = %d", p
->NumOfValidMpeTlbs
);
152 printk(KERN_DEBUG
"NumOfInvalidMpeTlbs = %d", p
->NumOfInvalidMpeTlbs
);
153 printk(KERN_DEBUG
"NumOfCorrectedMpeTlbs = %d", p
->NumOfCorrectedMpeTlbs
);
154 printk(KERN_DEBUG
"BERErrorCount = %d", p
->BERErrorCount
);
155 printk(KERN_DEBUG
"BERBitCount = %d", p
->BERBitCount
);
156 printk(KERN_DEBUG
"SmsToHostTxErrors = %d", p
->SmsToHostTxErrors
);
157 printk(KERN_DEBUG
"PreBER = %d", p
->PreBER
);
158 printk(KERN_DEBUG
"CellId = %d", p
->CellId
);
159 printk(KERN_DEBUG
"DvbhSrvIndHP = %d", p
->DvbhSrvIndHP
);
160 printk(KERN_DEBUG
"DvbhSrvIndLP = %d", p
->DvbhSrvIndLP
);
161 printk(KERN_DEBUG
"NumMPEReceived = %d", p
->NumMPEReceived
);
164 /* update reception data */
165 pReceptionData
->IsRfLocked
= p
->IsRfLocked
;
166 pReceptionData
->IsDemodLocked
= p
->IsDemodLocked
;
167 pReceptionData
->IsExternalLNAOn
= p
->IsExternalLNAOn
;
168 pReceptionData
->ModemState
= p
->ModemState
;
169 pReceptionData
->SNR
= p
->SNR
;
170 pReceptionData
->BER
= p
->BER
;
171 pReceptionData
->BERErrorCount
= p
->BERErrorCount
;
172 pReceptionData
->BERBitCount
= p
->BERBitCount
;
173 pReceptionData
->RSSI
= p
->RSSI
;
174 CORRECT_STAT_RSSI(*pReceptionData
);
175 pReceptionData
->InBandPwr
= p
->InBandPwr
;
176 pReceptionData
->CarrierOffset
= p
->CarrierOffset
;
177 pReceptionData
->ErrorTSPackets
= p
->ErrorTSPackets
;
178 pReceptionData
->TotalTSPackets
= p
->TotalTSPackets
;
181 static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_EX_S
*pReceptionData
,
182 struct SMSHOSTLIB_STATISTICS_ISDBT_ST
*p
)
187 printk(KERN_DEBUG
"IsRfLocked = %d", p
->IsRfLocked
);
188 printk(KERN_DEBUG
"IsDemodLocked = %d", p
->IsDemodLocked
);
189 printk(KERN_DEBUG
"IsExternalLNAOn = %d", p
->IsExternalLNAOn
);
190 printk(KERN_DEBUG
"SNR = %d", p
->SNR
);
191 printk(KERN_DEBUG
"RSSI = %d", p
->RSSI
);
192 printk(KERN_DEBUG
"InBandPwr = %d", p
->InBandPwr
);
193 printk(KERN_DEBUG
"CarrierOffset = %d", p
->CarrierOffset
);
194 printk(KERN_DEBUG
"Frequency = %d", p
->Frequency
);
195 printk(KERN_DEBUG
"Bandwidth = %d", p
->Bandwidth
);
196 printk(KERN_DEBUG
"TransmissionMode = %d", p
->TransmissionMode
);
197 printk(KERN_DEBUG
"ModemState = %d", p
->ModemState
);
198 printk(KERN_DEBUG
"GuardInterval = %d", p
->GuardInterval
);
199 printk(KERN_DEBUG
"SystemType = %d", p
->SystemType
);
200 printk(KERN_DEBUG
"PartialReception = %d", p
->PartialReception
);
201 printk(KERN_DEBUG
"NumOfLayers = %d", p
->NumOfLayers
);
202 printk(KERN_DEBUG
"SmsToHostTxErrors = %d", p
->SmsToHostTxErrors
);
204 for (i
= 0; i
< 3; i
++) {
205 printk(KERN_DEBUG
"%d: CodeRate = %d", i
, p
->LayerInfo
[i
].CodeRate
);
206 printk(KERN_DEBUG
"%d: Constellation = %d", i
, p
->LayerInfo
[i
].Constellation
);
207 printk(KERN_DEBUG
"%d: BER = %d", i
, p
->LayerInfo
[i
].BER
);
208 printk(KERN_DEBUG
"%d: BERErrorCount = %d", i
, p
->LayerInfo
[i
].BERErrorCount
);
209 printk(KERN_DEBUG
"%d: BERBitCount = %d", i
, p
->LayerInfo
[i
].BERBitCount
);
210 printk(KERN_DEBUG
"%d: PreBER = %d", i
, p
->LayerInfo
[i
].PreBER
);
211 printk(KERN_DEBUG
"%d: TS_PER = %d", i
, p
->LayerInfo
[i
].TS_PER
);
212 printk(KERN_DEBUG
"%d: ErrorTSPackets = %d", i
, p
->LayerInfo
[i
].ErrorTSPackets
);
213 printk(KERN_DEBUG
"%d: TotalTSPackets = %d", i
, p
->LayerInfo
[i
].TotalTSPackets
);
214 printk(KERN_DEBUG
"%d: TILdepthI = %d", i
, p
->LayerInfo
[i
].TILdepthI
);
215 printk(KERN_DEBUG
"%d: NumberOfSegments = %d", i
, p
->LayerInfo
[i
].NumberOfSegments
);
216 printk(KERN_DEBUG
"%d: TMCCErrors = %d", i
, p
->LayerInfo
[i
].TMCCErrors
);
220 /* update reception data */
221 pReceptionData
->IsRfLocked
= p
->IsRfLocked
;
222 pReceptionData
->IsDemodLocked
= p
->IsDemodLocked
;
223 pReceptionData
->IsExternalLNAOn
= p
->IsExternalLNAOn
;
224 pReceptionData
->ModemState
= p
->ModemState
;
225 pReceptionData
->SNR
= p
->SNR
;
226 pReceptionData
->BER
= p
->LayerInfo
[0].BER
;
227 pReceptionData
->BERErrorCount
= p
->LayerInfo
[0].BERErrorCount
;
228 pReceptionData
->BERBitCount
= p
->LayerInfo
[0].BERBitCount
;
229 pReceptionData
->RSSI
= p
->RSSI
;
230 CORRECT_STAT_RSSI(*pReceptionData
);
231 pReceptionData
->InBandPwr
= p
->InBandPwr
;
233 pReceptionData
->CarrierOffset
= p
->CarrierOffset
;
234 pReceptionData
->ErrorTSPackets
= p
->LayerInfo
[0].ErrorTSPackets
;
235 pReceptionData
->TotalTSPackets
= p
->LayerInfo
[0].TotalTSPackets
;
236 pReceptionData
->MFER
= 0;
239 if ((p
->LayerInfo
[0].TotalTSPackets
+
240 p
->LayerInfo
[0].ErrorTSPackets
) > 0) {
241 pReceptionData
->TS_PER
= (p
->LayerInfo
[0].ErrorTSPackets
242 * 100) / (p
->LayerInfo
[0].TotalTSPackets
243 + p
->LayerInfo
[0].ErrorTSPackets
);
245 pReceptionData
->TS_PER
= 0;
249 static void smsdvb_update_isdbt_stats_ex(struct RECEPTION_STATISTICS_EX_S
*pReceptionData
,
250 struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST
*p
)
255 printk(KERN_DEBUG
"IsRfLocked = %d", p
->IsRfLocked
);
256 printk(KERN_DEBUG
"IsDemodLocked = %d", p
->IsDemodLocked
);
257 printk(KERN_DEBUG
"IsExternalLNAOn = %d", p
->IsExternalLNAOn
);
258 printk(KERN_DEBUG
"SNR = %d", p
->SNR
);
259 printk(KERN_DEBUG
"RSSI = %d", p
->RSSI
);
260 printk(KERN_DEBUG
"InBandPwr = %d", p
->InBandPwr
);
261 printk(KERN_DEBUG
"CarrierOffset = %d", p
->CarrierOffset
);
262 printk(KERN_DEBUG
"Frequency = %d", p
->Frequency
);
263 printk(KERN_DEBUG
"Bandwidth = %d", p
->Bandwidth
);
264 printk(KERN_DEBUG
"TransmissionMode = %d", p
->TransmissionMode
);
265 printk(KERN_DEBUG
"ModemState = %d", p
->ModemState
);
266 printk(KERN_DEBUG
"GuardInterval = %d", p
->GuardInterval
);
267 printk(KERN_DEBUG
"SystemType = %d", p
->SystemType
);
268 printk(KERN_DEBUG
"PartialReception = %d", p
->PartialReception
);
269 printk(KERN_DEBUG
"NumOfLayers = %d", p
->NumOfLayers
);
270 printk(KERN_DEBUG
"SegmentNumber = %d", p
->SegmentNumber
);
271 printk(KERN_DEBUG
"TuneBW = %d", p
->TuneBW
);
272 for (i
= 0; i
< 3; i
++) {
273 printk(KERN_DEBUG
"%d: CodeRate = %d", i
, p
->LayerInfo
[i
].CodeRate
);
274 printk(KERN_DEBUG
"%d: Constellation = %d", i
, p
->LayerInfo
[i
].Constellation
);
275 printk(KERN_DEBUG
"%d: BER = %d", i
, p
->LayerInfo
[i
].BER
);
276 printk(KERN_DEBUG
"%d: BERErrorCount = %d", i
, p
->LayerInfo
[i
].BERErrorCount
);
277 printk(KERN_DEBUG
"%d: BERBitCount = %d", i
, p
->LayerInfo
[i
].BERBitCount
);
278 printk(KERN_DEBUG
"%d: PreBER = %d", i
, p
->LayerInfo
[i
].PreBER
);
279 printk(KERN_DEBUG
"%d: TS_PER = %d", i
, p
->LayerInfo
[i
].TS_PER
);
280 printk(KERN_DEBUG
"%d: ErrorTSPackets = %d", i
, p
->LayerInfo
[i
].ErrorTSPackets
);
281 printk(KERN_DEBUG
"%d: TotalTSPackets = %d", i
, p
->LayerInfo
[i
].TotalTSPackets
);
282 printk(KERN_DEBUG
"%d: TILdepthI = %d", i
, p
->LayerInfo
[i
].TILdepthI
);
283 printk(KERN_DEBUG
"%d: NumberOfSegments = %d", i
, p
->LayerInfo
[i
].NumberOfSegments
);
284 printk(KERN_DEBUG
"%d: TMCCErrors = %d", i
, p
->LayerInfo
[i
].TMCCErrors
);
288 /* update reception data */
289 pReceptionData
->IsRfLocked
= p
->IsRfLocked
;
290 pReceptionData
->IsDemodLocked
= p
->IsDemodLocked
;
291 pReceptionData
->IsExternalLNAOn
= p
->IsExternalLNAOn
;
292 pReceptionData
->ModemState
= p
->ModemState
;
293 pReceptionData
->SNR
= p
->SNR
;
294 pReceptionData
->BER
= p
->LayerInfo
[0].BER
;
295 pReceptionData
->BERErrorCount
= p
->LayerInfo
[0].BERErrorCount
;
296 pReceptionData
->BERBitCount
= p
->LayerInfo
[0].BERBitCount
;
297 pReceptionData
->RSSI
= p
->RSSI
;
298 CORRECT_STAT_RSSI(*pReceptionData
);
299 pReceptionData
->InBandPwr
= p
->InBandPwr
;
301 pReceptionData
->CarrierOffset
= p
->CarrierOffset
;
302 pReceptionData
->ErrorTSPackets
= p
->LayerInfo
[0].ErrorTSPackets
;
303 pReceptionData
->TotalTSPackets
= p
->LayerInfo
[0].TotalTSPackets
;
304 pReceptionData
->MFER
= 0;
307 if ((p
->LayerInfo
[0].TotalTSPackets
+
308 p
->LayerInfo
[0].ErrorTSPackets
) > 0) {
309 pReceptionData
->TS_PER
= (p
->LayerInfo
[0].ErrorTSPackets
310 * 100) / (p
->LayerInfo
[0].TotalTSPackets
311 + p
->LayerInfo
[0].ErrorTSPackets
);
313 pReceptionData
->TS_PER
= 0;
317 static int smsdvb_onresponse(void *context
, struct smscore_buffer_t
*cb
)
319 struct smsdvb_client_t
*client
= (struct smsdvb_client_t
*) context
;
320 struct SmsMsgHdr_ST
*phdr
= (struct SmsMsgHdr_ST
*) (((u8
*) cb
->p
)
322 u32
*pMsgData
= (u32
*) phdr
+ 1;
323 /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/
324 bool is_status_update
= false;
326 switch (phdr
->msgType
) {
327 case MSG_SMS_DVBT_BDA_DATA
:
328 dvb_dmx_swfilter(&client
->demux
, (u8
*)(phdr
+ 1),
329 cb
->size
- sizeof(struct SmsMsgHdr_ST
));
332 case MSG_SMS_RF_TUNE_RES
:
333 case MSG_SMS_ISDBT_TUNE_RES
:
334 complete(&client
->tune_done
);
337 case MSG_SMS_SIGNAL_DETECTED_IND
:
338 client
->sms_stat_dvb
.TransmissionData
.IsDemodLocked
= true;
339 is_status_update
= true;
342 case MSG_SMS_NO_SIGNAL_IND
:
343 client
->sms_stat_dvb
.TransmissionData
.IsDemodLocked
= false;
344 is_status_update
= true;
347 case MSG_SMS_TRANSMISSION_IND
: {
350 memcpy(&client
->sms_stat_dvb
.TransmissionData
, pMsgData
,
351 sizeof(struct TRANSMISSION_STATISTICS_S
));
355 * FIXME: newer driver doesn't have those fixes
356 * Are those firmware-specific stuff?
359 /* Mo need to correct guard interval
360 * (as opposed to old statistics message).
362 CORRECT_STAT_BANDWIDTH(client
->sms_stat_dvb
.TransmissionData
);
363 CORRECT_STAT_TRANSMISSON_MODE(
364 client
->sms_stat_dvb
.TransmissionData
);
366 is_status_update
= true;
369 case MSG_SMS_HO_PER_SLICES_IND
: {
370 struct RECEPTION_STATISTICS_EX_S
*pReceptionData
=
371 &client
->sms_stat_dvb
.ReceptionData
;
372 struct SRVM_SIGNAL_STATUS_S SignalStatusData
;
375 SignalStatusData
.result
= pMsgData
[0];
376 SignalStatusData
.snr
= pMsgData
[1];
377 SignalStatusData
.inBandPower
= (s32
) pMsgData
[2];
378 SignalStatusData
.tsPackets
= pMsgData
[3];
379 SignalStatusData
.etsPackets
= pMsgData
[4];
380 SignalStatusData
.constellation
= pMsgData
[5];
381 SignalStatusData
.hpCode
= pMsgData
[6];
382 SignalStatusData
.tpsSrvIndLP
= pMsgData
[7] & 0x03;
383 SignalStatusData
.tpsSrvIndHP
= pMsgData
[8] & 0x03;
384 SignalStatusData
.cellId
= pMsgData
[9] & 0xFFFF;
385 SignalStatusData
.reason
= pMsgData
[10];
386 SignalStatusData
.requestId
= pMsgData
[11];
387 pReceptionData
->IsRfLocked
= pMsgData
[16];
388 pReceptionData
->IsDemodLocked
= pMsgData
[17];
389 pReceptionData
->ModemState
= pMsgData
[12];
390 pReceptionData
->SNR
= pMsgData
[1];
391 pReceptionData
->BER
= pMsgData
[13];
392 pReceptionData
->RSSI
= pMsgData
[14];
393 CORRECT_STAT_RSSI(client
->sms_stat_dvb
.ReceptionData
);
395 pReceptionData
->InBandPwr
= (s32
) pMsgData
[2];
396 pReceptionData
->CarrierOffset
= (s32
) pMsgData
[15];
397 pReceptionData
->TotalTSPackets
= pMsgData
[3];
398 pReceptionData
->ErrorTSPackets
= pMsgData
[4];
401 if ((SignalStatusData
.tsPackets
+ SignalStatusData
.etsPackets
)
403 pReceptionData
->TS_PER
= (SignalStatusData
.etsPackets
404 * 100) / (SignalStatusData
.tsPackets
405 + SignalStatusData
.etsPackets
);
407 pReceptionData
->TS_PER
= 0;
410 pReceptionData
->BERBitCount
= pMsgData
[18];
411 pReceptionData
->BERErrorCount
= pMsgData
[19];
413 pReceptionData
->MRC_SNR
= pMsgData
[20];
414 pReceptionData
->MRC_InBandPwr
= pMsgData
[21];
415 pReceptionData
->MRC_RSSI
= pMsgData
[22];
417 is_status_update
= true;
420 case MSG_SMS_GET_STATISTICS_RES
: {
422 struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt
;
423 struct SmsMsgStatisticsInfo_ST dvb
;
424 } *p
= (void *) (phdr
+ 1);
425 struct RECEPTION_STATISTICS_EX_S
*pReceptionData
=
426 &client
->sms_stat_dvb
.ReceptionData
;
428 is_status_update
= true;
430 switch (smscore_get_device_mode(client
->coredev
)) {
431 case DEVICE_MODE_ISDBT
:
432 case DEVICE_MODE_ISDBT_BDA
:
433 smsdvb_update_isdbt_stats(pReceptionData
, &p
->isdbt
);
436 smsdvb_update_dvb_stats(pReceptionData
, &p
->dvb
.Stat
);
438 if (!pReceptionData
->IsDemodLocked
) {
439 pReceptionData
->SNR
= 0;
440 pReceptionData
->BER
= 0;
441 pReceptionData
->BERErrorCount
= 0;
442 pReceptionData
->InBandPwr
= 0;
443 pReceptionData
->ErrorTSPackets
= 0;
448 case MSG_SMS_GET_STATISTICS_EX_RES
: {
450 struct SMSHOSTLIB_STATISTICS_ISDBT_EX_ST isdbt
;
451 struct SMSHOSTLIB_STATISTICS_ST dvb
;
452 } *p
= (void *) (phdr
+ 1);
453 struct RECEPTION_STATISTICS_EX_S
*pReceptionData
=
454 &client
->sms_stat_dvb
.ReceptionData
;
456 is_status_update
= true;
458 switch (smscore_get_device_mode(client
->coredev
)) {
459 case DEVICE_MODE_ISDBT
:
460 case DEVICE_MODE_ISDBT_BDA
:
461 smsdvb_update_isdbt_stats_ex(pReceptionData
, &p
->isdbt
);
464 smsdvb_update_dvb_stats(pReceptionData
, &p
->dvb
);
466 if (!pReceptionData
->IsDemodLocked
) {
467 pReceptionData
->SNR
= 0;
468 pReceptionData
->BER
= 0;
469 pReceptionData
->BERErrorCount
= 0;
470 pReceptionData
->InBandPwr
= 0;
471 pReceptionData
->ErrorTSPackets
= 0;
477 sms_info("message not handled");
479 smscore_putbuffer(client
->coredev
, cb
);
481 if (is_status_update
) {
482 if (client
->sms_stat_dvb
.ReceptionData
.IsDemodLocked
) {
483 client
->fe_status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
484 | FE_HAS_VITERBI
| FE_HAS_SYNC
| FE_HAS_LOCK
;
485 sms_board_dvb3_event(client
, DVB3_EVENT_FE_LOCK
);
486 if (client
->sms_stat_dvb
.ReceptionData
.ErrorTSPackets
488 sms_board_dvb3_event(client
, DVB3_EVENT_UNC_OK
);
490 sms_board_dvb3_event(client
,
494 if (client
->sms_stat_dvb
.ReceptionData
.IsRfLocked
)
495 client
->fe_status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
;
497 client
->fe_status
= 0;
498 sms_board_dvb3_event(client
, DVB3_EVENT_FE_UNLOCK
);
500 complete(&client
->stats_done
);
506 static void smsdvb_unregister_client(struct smsdvb_client_t
*client
)
508 /* must be called under clientslock */
510 list_del(&client
->entry
);
512 smscore_unregister_client(client
->smsclient
);
513 dvb_unregister_frontend(&client
->frontend
);
514 dvb_dmxdev_release(&client
->dmxdev
);
515 dvb_dmx_release(&client
->demux
);
516 dvb_unregister_adapter(&client
->adapter
);
520 static void smsdvb_onremove(void *context
)
522 kmutex_lock(&g_smsdvb_clientslock
);
524 smsdvb_unregister_client((struct smsdvb_client_t
*) context
);
526 kmutex_unlock(&g_smsdvb_clientslock
);
529 static int smsdvb_start_feed(struct dvb_demux_feed
*feed
)
531 struct smsdvb_client_t
*client
=
532 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
533 struct SmsMsgData_ST PidMsg
;
535 sms_debug("add pid %d(%x)",
536 feed
->pid
, feed
->pid
);
538 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
539 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
540 PidMsg
.xMsgHeader
.msgFlags
= 0;
541 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_ADD_PID_FILTER_REQ
;
542 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
543 PidMsg
.msgData
[0] = feed
->pid
;
545 return smsclient_sendrequest(client
->smsclient
,
546 &PidMsg
, sizeof(PidMsg
));
549 static int smsdvb_stop_feed(struct dvb_demux_feed
*feed
)
551 struct smsdvb_client_t
*client
=
552 container_of(feed
->demux
, struct smsdvb_client_t
, demux
);
553 struct SmsMsgData_ST PidMsg
;
555 sms_debug("remove pid %d(%x)",
556 feed
->pid
, feed
->pid
);
558 PidMsg
.xMsgHeader
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
559 PidMsg
.xMsgHeader
.msgDstId
= HIF_TASK
;
560 PidMsg
.xMsgHeader
.msgFlags
= 0;
561 PidMsg
.xMsgHeader
.msgType
= MSG_SMS_REMOVE_PID_FILTER_REQ
;
562 PidMsg
.xMsgHeader
.msgLength
= sizeof(PidMsg
);
563 PidMsg
.msgData
[0] = feed
->pid
;
565 return smsclient_sendrequest(client
->smsclient
,
566 &PidMsg
, sizeof(PidMsg
));
569 static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t
*client
,
570 void *buffer
, size_t size
,
571 struct completion
*completion
)
575 rc
= smsclient_sendrequest(client
->smsclient
, buffer
, size
);
579 return wait_for_completion_timeout(completion
,
580 msecs_to_jiffies(2000)) ?
584 static int smsdvb_send_statistics_request(struct smsdvb_client_t
*client
)
587 struct SmsMsgHdr_ST Msg
;
590 Msg
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
591 Msg
.msgDstId
= HIF_TASK
;
593 Msg
.msgLength
= sizeof(Msg
);
596 * Check for firmware version, to avoid breaking for old cards
598 if (client
->coredev
->fw_version
>= 0x800)
599 Msg
.msgType
= MSG_SMS_GET_STATISTICS_EX_REQ
;
601 Msg
.msgType
= MSG_SMS_GET_STATISTICS_REQ
;
603 rc
= smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
604 &client
->stats_done
);
609 static inline int led_feedback(struct smsdvb_client_t
*client
)
611 if (client
->fe_status
& FE_HAS_LOCK
)
612 return sms_board_led_feedback(client
->coredev
,
613 (client
->sms_stat_dvb
.ReceptionData
.BER
614 == 0) ? SMS_LED_HI
: SMS_LED_LO
);
616 return sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
619 static int smsdvb_read_status(struct dvb_frontend
*fe
, fe_status_t
*stat
)
622 struct smsdvb_client_t
*client
;
623 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
625 rc
= smsdvb_send_statistics_request(client
);
627 *stat
= client
->fe_status
;
629 led_feedback(client
);
634 static int smsdvb_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
637 struct smsdvb_client_t
*client
;
638 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
640 rc
= smsdvb_send_statistics_request(client
);
642 *ber
= client
->sms_stat_dvb
.ReceptionData
.BER
;
644 led_feedback(client
);
649 static int smsdvb_read_signal_strength(struct dvb_frontend
*fe
, u16
*strength
)
653 struct smsdvb_client_t
*client
;
654 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
656 rc
= smsdvb_send_statistics_request(client
);
658 if (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
< -95)
660 else if (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
> -29)
664 (client
->sms_stat_dvb
.ReceptionData
.InBandPwr
667 led_feedback(client
);
672 static int smsdvb_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
675 struct smsdvb_client_t
*client
;
676 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
678 rc
= smsdvb_send_statistics_request(client
);
680 *snr
= client
->sms_stat_dvb
.ReceptionData
.SNR
;
682 led_feedback(client
);
687 static int smsdvb_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
690 struct smsdvb_client_t
*client
;
691 client
= container_of(fe
, struct smsdvb_client_t
, frontend
);
693 rc
= smsdvb_send_statistics_request(client
);
695 *ucblocks
= client
->sms_stat_dvb
.ReceptionData
.ErrorTSPackets
;
697 led_feedback(client
);
702 static int smsdvb_get_tune_settings(struct dvb_frontend
*fe
,
703 struct dvb_frontend_tune_settings
*tune
)
707 tune
->min_delay_ms
= 400;
708 tune
->step_size
= 250000;
713 static int smsdvb_dvbt_set_frontend(struct dvb_frontend
*fe
)
715 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
716 struct smsdvb_client_t
*client
=
717 container_of(fe
, struct smsdvb_client_t
, frontend
);
720 struct SmsMsgHdr_ST Msg
;
726 client
->fe_status
= FE_HAS_SIGNAL
;
727 client
->event_fe_state
= -1;
728 client
->event_unc_state
= -1;
729 fe
->dtv_property_cache
.delivery_system
= SYS_DVBT
;
731 Msg
.Msg
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
732 Msg
.Msg
.msgDstId
= HIF_TASK
;
733 Msg
.Msg
.msgFlags
= 0;
734 Msg
.Msg
.msgType
= MSG_SMS_RF_TUNE_REQ
;
735 Msg
.Msg
.msgLength
= sizeof(Msg
);
736 Msg
.Data
[0] = c
->frequency
;
737 Msg
.Data
[2] = 12000000;
739 sms_info("%s: freq %d band %d", __func__
, c
->frequency
,
742 switch (c
->bandwidth_hz
/ 1000000) {
744 Msg
.Data
[1] = BW_8_MHZ
;
747 Msg
.Data
[1] = BW_7_MHZ
;
750 Msg
.Data
[1] = BW_6_MHZ
;
757 /* Disable LNA, if any. An error is returned if no LNA is present */
758 ret
= sms_board_lna_control(client
->coredev
, 0);
762 /* tune with LNA off at first */
763 ret
= smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
766 smsdvb_read_status(fe
, &status
);
768 if (status
& FE_HAS_LOCK
)
771 /* previous tune didn't lock - enable LNA and tune again */
772 sms_board_lna_control(client
->coredev
, 1);
775 return smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
779 static int smsdvb_isdbt_set_frontend(struct dvb_frontend
*fe
)
781 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
782 struct smsdvb_client_t
*client
=
783 container_of(fe
, struct smsdvb_client_t
, frontend
);
784 int board_id
= smscore_get_board_id(client
->coredev
);
785 struct sms_board
*board
= sms_get_board(board_id
);
786 enum sms_device_type_st type
= board
->type
;
789 struct SmsMsgHdr_ST Msg
;
793 fe
->dtv_property_cache
.delivery_system
= SYS_ISDBT
;
795 Msg
.Msg
.msgSrcId
= DVBT_BDA_CONTROL_MSG_ID
;
796 Msg
.Msg
.msgDstId
= HIF_TASK
;
797 Msg
.Msg
.msgFlags
= 0;
798 Msg
.Msg
.msgType
= MSG_SMS_ISDBT_TUNE_REQ
;
799 Msg
.Msg
.msgLength
= sizeof(Msg
);
801 if (c
->isdbt_sb_segment_idx
== -1)
802 c
->isdbt_sb_segment_idx
= 0;
804 if (!c
->isdbt_layer_enabled
)
805 c
->isdbt_layer_enabled
= 7;
807 Msg
.Data
[0] = c
->frequency
;
808 Msg
.Data
[1] = BW_ISDBT_1SEG
;
809 Msg
.Data
[2] = 12000000;
810 Msg
.Data
[3] = c
->isdbt_sb_segment_idx
;
812 if (c
->isdbt_partial_reception
) {
813 if ((type
== SMS_PELE
|| type
== SMS_RIO
) &&
814 c
->isdbt_sb_segment_count
> 3)
815 Msg
.Data
[1] = BW_ISDBT_13SEG
;
816 else if (c
->isdbt_sb_segment_count
> 1)
817 Msg
.Data
[1] = BW_ISDBT_3SEG
;
818 } else if (type
== SMS_PELE
|| type
== SMS_RIO
)
819 Msg
.Data
[1] = BW_ISDBT_13SEG
;
821 c
->bandwidth_hz
= 6000000;
823 sms_info("%s: freq %d segwidth %d segindex %d\n", __func__
,
824 c
->frequency
, c
->isdbt_sb_segment_count
,
825 c
->isdbt_sb_segment_idx
);
827 /* Disable LNA, if any. An error is returned if no LNA is present */
828 ret
= sms_board_lna_control(client
->coredev
, 0);
832 /* tune with LNA off at first */
833 ret
= smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
836 smsdvb_read_status(fe
, &status
);
838 if (status
& FE_HAS_LOCK
)
841 /* previous tune didn't lock - enable LNA and tune again */
842 sms_board_lna_control(client
->coredev
, 1);
844 return smsdvb_sendrequest_and_wait(client
, &Msg
, sizeof(Msg
),
848 static int smsdvb_set_frontend(struct dvb_frontend
*fe
)
850 struct smsdvb_client_t
*client
=
851 container_of(fe
, struct smsdvb_client_t
, frontend
);
852 struct smscore_device_t
*coredev
= client
->coredev
;
854 switch (smscore_get_device_mode(coredev
)) {
855 case DEVICE_MODE_DVBT
:
856 case DEVICE_MODE_DVBT_BDA
:
857 return smsdvb_dvbt_set_frontend(fe
);
858 case DEVICE_MODE_ISDBT
:
859 case DEVICE_MODE_ISDBT_BDA
:
860 return smsdvb_isdbt_set_frontend(fe
);
866 static int smsdvb_get_frontend(struct dvb_frontend
*fe
)
868 struct dtv_frontend_properties
*fep
= &fe
->dtv_property_cache
;
869 struct smsdvb_client_t
*client
=
870 container_of(fe
, struct smsdvb_client_t
, frontend
);
871 struct smscore_device_t
*coredev
= client
->coredev
;
872 struct TRANSMISSION_STATISTICS_S
*td
=
873 &client
->sms_stat_dvb
.TransmissionData
;
875 switch (smscore_get_device_mode(coredev
)) {
876 case DEVICE_MODE_DVBT
:
877 case DEVICE_MODE_DVBT_BDA
:
878 fep
->frequency
= td
->Frequency
;
880 switch (td
->Bandwidth
) {
882 fep
->bandwidth_hz
= 6000000;
885 fep
->bandwidth_hz
= 7000000;
888 fep
->bandwidth_hz
= 8000000;
892 switch (td
->TransmissionMode
) {
894 fep
->transmission_mode
= TRANSMISSION_MODE_2K
;
897 fep
->transmission_mode
= TRANSMISSION_MODE_8K
;
900 switch (td
->GuardInterval
) {
902 fep
->guard_interval
= GUARD_INTERVAL_1_32
;
905 fep
->guard_interval
= GUARD_INTERVAL_1_16
;
908 fep
->guard_interval
= GUARD_INTERVAL_1_8
;
911 fep
->guard_interval
= GUARD_INTERVAL_1_4
;
915 switch (td
->CodeRate
) {
917 fep
->code_rate_HP
= FEC_1_2
;
920 fep
->code_rate_HP
= FEC_2_3
;
923 fep
->code_rate_HP
= FEC_3_4
;
926 fep
->code_rate_HP
= FEC_5_6
;
929 fep
->code_rate_HP
= FEC_7_8
;
933 switch (td
->LPCodeRate
) {
935 fep
->code_rate_LP
= FEC_1_2
;
938 fep
->code_rate_LP
= FEC_2_3
;
941 fep
->code_rate_LP
= FEC_3_4
;
944 fep
->code_rate_LP
= FEC_5_6
;
947 fep
->code_rate_LP
= FEC_7_8
;
951 switch (td
->Constellation
) {
953 fep
->modulation
= QPSK
;
956 fep
->modulation
= QAM_16
;
959 fep
->modulation
= QAM_64
;
963 switch (td
->Hierarchy
) {
965 fep
->hierarchy
= HIERARCHY_NONE
;
968 fep
->hierarchy
= HIERARCHY_1
;
971 fep
->hierarchy
= HIERARCHY_2
;
974 fep
->hierarchy
= HIERARCHY_4
;
978 fep
->inversion
= INVERSION_AUTO
;
980 case DEVICE_MODE_ISDBT
:
981 case DEVICE_MODE_ISDBT_BDA
:
982 fep
->frequency
= td
->Frequency
;
983 fep
->bandwidth_hz
= 6000000;
984 /* todo: retrive the other parameters */
993 static int smsdvb_init(struct dvb_frontend
*fe
)
995 struct smsdvb_client_t
*client
=
996 container_of(fe
, struct smsdvb_client_t
, frontend
);
998 sms_board_power(client
->coredev
, 1);
1000 sms_board_dvb3_event(client
, DVB3_EVENT_INIT
);
1004 static int smsdvb_sleep(struct dvb_frontend
*fe
)
1006 struct smsdvb_client_t
*client
=
1007 container_of(fe
, struct smsdvb_client_t
, frontend
);
1009 sms_board_led_feedback(client
->coredev
, SMS_LED_OFF
);
1010 sms_board_power(client
->coredev
, 0);
1012 sms_board_dvb3_event(client
, DVB3_EVENT_SLEEP
);
1017 static void smsdvb_release(struct dvb_frontend
*fe
)
1022 static struct dvb_frontend_ops smsdvb_fe_ops
= {
1024 .name
= "Siano Mobile Digital MDTV Receiver",
1025 .frequency_min
= 44250000,
1026 .frequency_max
= 867250000,
1027 .frequency_stepsize
= 250000,
1028 .caps
= FE_CAN_INVERSION_AUTO
|
1029 FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
1030 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
1031 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
1032 FE_CAN_QAM_AUTO
| FE_CAN_TRANSMISSION_MODE_AUTO
|
1033 FE_CAN_GUARD_INTERVAL_AUTO
|
1035 FE_CAN_HIERARCHY_AUTO
,
1038 .release
= smsdvb_release
,
1040 .set_frontend
= smsdvb_set_frontend
,
1041 .get_frontend
= smsdvb_get_frontend
,
1042 .get_tune_settings
= smsdvb_get_tune_settings
,
1044 .read_status
= smsdvb_read_status
,
1045 .read_ber
= smsdvb_read_ber
,
1046 .read_signal_strength
= smsdvb_read_signal_strength
,
1047 .read_snr
= smsdvb_read_snr
,
1048 .read_ucblocks
= smsdvb_read_ucblocks
,
1050 .init
= smsdvb_init
,
1051 .sleep
= smsdvb_sleep
,
1054 static int smsdvb_hotplug(struct smscore_device_t
*coredev
,
1055 struct device
*device
, int arrival
)
1057 struct smsclient_params_t params
;
1058 struct smsdvb_client_t
*client
;
1061 /* device removal handled by onremove callback */
1064 client
= kzalloc(sizeof(struct smsdvb_client_t
), GFP_KERNEL
);
1066 sms_err("kmalloc() failed");
1070 /* register dvb adapter */
1071 rc
= dvb_register_adapter(&client
->adapter
,
1073 smscore_get_board_id(coredev
))->name
,
1074 THIS_MODULE
, device
, adapter_nr
);
1076 sms_err("dvb_register_adapter() failed %d", rc
);
1080 /* init dvb demux */
1081 client
->demux
.dmx
.capabilities
= DMX_TS_FILTERING
;
1082 client
->demux
.filternum
= 32; /* todo: nova ??? */
1083 client
->demux
.feednum
= 32;
1084 client
->demux
.start_feed
= smsdvb_start_feed
;
1085 client
->demux
.stop_feed
= smsdvb_stop_feed
;
1087 rc
= dvb_dmx_init(&client
->demux
);
1089 sms_err("dvb_dmx_init failed %d", rc
);
1094 client
->dmxdev
.filternum
= 32;
1095 client
->dmxdev
.demux
= &client
->demux
.dmx
;
1096 client
->dmxdev
.capabilities
= 0;
1098 rc
= dvb_dmxdev_init(&client
->dmxdev
, &client
->adapter
);
1100 sms_err("dvb_dmxdev_init failed %d", rc
);
1104 /* init and register frontend */
1105 memcpy(&client
->frontend
.ops
, &smsdvb_fe_ops
,
1106 sizeof(struct dvb_frontend_ops
));
1108 switch (smscore_get_device_mode(coredev
)) {
1109 case DEVICE_MODE_DVBT
:
1110 case DEVICE_MODE_DVBT_BDA
:
1111 client
->frontend
.ops
.delsys
[0] = SYS_DVBT
;
1113 case DEVICE_MODE_ISDBT
:
1114 case DEVICE_MODE_ISDBT_BDA
:
1115 client
->frontend
.ops
.delsys
[0] = SYS_ISDBT
;
1119 rc
= dvb_register_frontend(&client
->adapter
, &client
->frontend
);
1121 sms_err("frontend registration failed %d", rc
);
1122 goto frontend_error
;
1125 params
.initial_id
= 1;
1126 params
.data_type
= MSG_SMS_DVBT_BDA_DATA
;
1127 params
.onresponse_handler
= smsdvb_onresponse
;
1128 params
.onremove_handler
= smsdvb_onremove
;
1129 params
.context
= client
;
1131 rc
= smscore_register_client(coredev
, ¶ms
, &client
->smsclient
);
1133 sms_err("smscore_register_client() failed %d", rc
);
1137 client
->coredev
= coredev
;
1139 init_completion(&client
->tune_done
);
1140 init_completion(&client
->stats_done
);
1142 kmutex_lock(&g_smsdvb_clientslock
);
1144 list_add(&client
->entry
, &g_smsdvb_clients
);
1146 kmutex_unlock(&g_smsdvb_clientslock
);
1148 client
->event_fe_state
= -1;
1149 client
->event_unc_state
= -1;
1150 sms_board_dvb3_event(client
, DVB3_EVENT_HOTPLUG
);
1152 sms_info("success");
1153 sms_board_setup(coredev
);
1158 dvb_unregister_frontend(&client
->frontend
);
1161 dvb_dmxdev_release(&client
->dmxdev
);
1164 dvb_dmx_release(&client
->demux
);
1167 dvb_unregister_adapter(&client
->adapter
);
1174 static int __init
smsdvb_module_init(void)
1178 INIT_LIST_HEAD(&g_smsdvb_clients
);
1179 kmutex_init(&g_smsdvb_clientslock
);
1181 rc
= smscore_register_hotplug(smsdvb_hotplug
);
1188 static void __exit
smsdvb_module_exit(void)
1190 smscore_unregister_hotplug(smsdvb_hotplug
);
1192 kmutex_lock(&g_smsdvb_clientslock
);
1194 while (!list_empty(&g_smsdvb_clients
))
1195 smsdvb_unregister_client(
1196 (struct smsdvb_client_t
*) g_smsdvb_clients
.next
);
1198 kmutex_unlock(&g_smsdvb_clientslock
);
1201 module_init(smsdvb_module_init
);
1202 module_exit(smsdvb_module_exit
);
1204 MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
1205 MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
1206 MODULE_LICENSE("GPL");