1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /**************************************************************** |
3 | |
4 | Siano Mobile Silicon, Inc. |
5 | MDTV receiver kernel modules. |
6 | Copyright (C) 2006-2008, Uri Shkolnik |
7 | |
8 | |
9 | ****************************************************************/ |
10 | |
11 | #include "smscoreapi.h" |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/init.h> |
16 | #include <asm/div64.h> |
17 | |
18 | #include <media/dmxdev.h> |
19 | #include <media/dvbdev.h> |
20 | #include <media/dvb_demux.h> |
21 | #include <media/dvb_frontend.h> |
22 | |
23 | #include "sms-cards.h" |
24 | |
25 | #include "smsdvb.h" |
26 | |
27 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
28 | |
29 | static LIST_HEAD(g_smsdvb_clients); |
30 | static DEFINE_MUTEX(g_smsdvb_clientslock); |
31 | |
32 | static u32 sms_to_guard_interval_table[] = { |
33 | [0] = GUARD_INTERVAL_1_32, |
34 | [1] = GUARD_INTERVAL_1_16, |
35 | [2] = GUARD_INTERVAL_1_8, |
36 | [3] = GUARD_INTERVAL_1_4, |
37 | }; |
38 | |
39 | static u32 sms_to_code_rate_table[] = { |
40 | [0] = FEC_1_2, |
41 | [1] = FEC_2_3, |
42 | [2] = FEC_3_4, |
43 | [3] = FEC_5_6, |
44 | [4] = FEC_7_8, |
45 | }; |
46 | |
47 | |
48 | static u32 sms_to_hierarchy_table[] = { |
49 | [0] = HIERARCHY_NONE, |
50 | [1] = HIERARCHY_1, |
51 | [2] = HIERARCHY_2, |
52 | [3] = HIERARCHY_4, |
53 | }; |
54 | |
55 | static u32 sms_to_modulation_table[] = { |
56 | [0] = QPSK, |
57 | [1] = QAM_16, |
58 | [2] = QAM_64, |
59 | [3] = DQPSK, |
60 | }; |
61 | |
62 | |
63 | /* Events that may come from DVB v3 adapter */ |
64 | static void sms_board_dvb3_event(struct smsdvb_client_t *client, |
65 | enum SMS_DVB3_EVENTS event) { |
66 | |
67 | struct smscore_device_t *coredev = client->coredev; |
68 | switch (event) { |
69 | case DVB3_EVENT_INIT: |
70 | pr_debug("DVB3_EVENT_INIT\n" ); |
71 | sms_board_event(coredev, gevent: BOARD_EVENT_BIND); |
72 | break; |
73 | case DVB3_EVENT_SLEEP: |
74 | pr_debug("DVB3_EVENT_SLEEP\n" ); |
75 | sms_board_event(coredev, gevent: BOARD_EVENT_POWER_SUSPEND); |
76 | break; |
77 | case DVB3_EVENT_HOTPLUG: |
78 | pr_debug("DVB3_EVENT_HOTPLUG\n" ); |
79 | sms_board_event(coredev, gevent: BOARD_EVENT_POWER_INIT); |
80 | break; |
81 | case DVB3_EVENT_FE_LOCK: |
82 | if (client->event_fe_state != DVB3_EVENT_FE_LOCK) { |
83 | client->event_fe_state = DVB3_EVENT_FE_LOCK; |
84 | pr_debug("DVB3_EVENT_FE_LOCK\n" ); |
85 | sms_board_event(coredev, gevent: BOARD_EVENT_FE_LOCK); |
86 | } |
87 | break; |
88 | case DVB3_EVENT_FE_UNLOCK: |
89 | if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) { |
90 | client->event_fe_state = DVB3_EVENT_FE_UNLOCK; |
91 | pr_debug("DVB3_EVENT_FE_UNLOCK\n" ); |
92 | sms_board_event(coredev, gevent: BOARD_EVENT_FE_UNLOCK); |
93 | } |
94 | break; |
95 | case DVB3_EVENT_UNC_OK: |
96 | if (client->event_unc_state != DVB3_EVENT_UNC_OK) { |
97 | client->event_unc_state = DVB3_EVENT_UNC_OK; |
98 | pr_debug("DVB3_EVENT_UNC_OK\n" ); |
99 | sms_board_event(coredev, gevent: BOARD_EVENT_MULTIPLEX_OK); |
100 | } |
101 | break; |
102 | case DVB3_EVENT_UNC_ERR: |
103 | if (client->event_unc_state != DVB3_EVENT_UNC_ERR) { |
104 | client->event_unc_state = DVB3_EVENT_UNC_ERR; |
105 | pr_debug("DVB3_EVENT_UNC_ERR\n" ); |
106 | sms_board_event(coredev, gevent: BOARD_EVENT_MULTIPLEX_ERRORS); |
107 | } |
108 | break; |
109 | |
110 | default: |
111 | pr_err("Unknown dvb3 api event\n" ); |
112 | break; |
113 | } |
114 | } |
115 | |
116 | static void smsdvb_stats_not_ready(struct dvb_frontend *fe) |
117 | { |
118 | struct smsdvb_client_t *client = |
119 | container_of(fe, struct smsdvb_client_t, frontend); |
120 | struct smscore_device_t *coredev = client->coredev; |
121 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
122 | int i, n_layers; |
123 | |
124 | switch (smscore_get_device_mode(coredev)) { |
125 | case DEVICE_MODE_ISDBT: |
126 | case DEVICE_MODE_ISDBT_BDA: |
127 | n_layers = 4; |
128 | break; |
129 | default: |
130 | n_layers = 1; |
131 | } |
132 | |
133 | /* Global stats */ |
134 | c->strength.len = 1; |
135 | c->cnr.len = 1; |
136 | c->strength.stat[0].scale = FE_SCALE_DECIBEL; |
137 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
138 | |
139 | /* Per-layer stats */ |
140 | c->post_bit_error.len = n_layers; |
141 | c->post_bit_count.len = n_layers; |
142 | c->block_error.len = n_layers; |
143 | c->block_count.len = n_layers; |
144 | |
145 | /* |
146 | * Put all of them at FE_SCALE_NOT_AVAILABLE. They're dynamically |
147 | * changed when the stats become available. |
148 | */ |
149 | for (i = 0; i < n_layers; i++) { |
150 | c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; |
151 | c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; |
152 | c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; |
153 | c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; |
154 | } |
155 | } |
156 | |
157 | static inline int sms_to_mode(u32 mode) |
158 | { |
159 | switch (mode) { |
160 | case 2: |
161 | return TRANSMISSION_MODE_2K; |
162 | case 4: |
163 | return TRANSMISSION_MODE_4K; |
164 | case 8: |
165 | return TRANSMISSION_MODE_8K; |
166 | } |
167 | return TRANSMISSION_MODE_AUTO; |
168 | } |
169 | |
170 | static inline int sms_to_isdbt_mode(u32 mode) |
171 | { |
172 | switch (mode) { |
173 | case 1: |
174 | return TRANSMISSION_MODE_2K; |
175 | case 2: |
176 | return TRANSMISSION_MODE_4K; |
177 | case 3: |
178 | return TRANSMISSION_MODE_8K; |
179 | } |
180 | return TRANSMISSION_MODE_AUTO; |
181 | } |
182 | |
183 | static inline int sms_to_isdbt_guard_interval(u32 interval) |
184 | { |
185 | switch (interval) { |
186 | case 4: |
187 | return GUARD_INTERVAL_1_4; |
188 | case 8: |
189 | return GUARD_INTERVAL_1_8; |
190 | case 16: |
191 | return GUARD_INTERVAL_1_16; |
192 | case 32: |
193 | return GUARD_INTERVAL_1_32; |
194 | } |
195 | return GUARD_INTERVAL_AUTO; |
196 | } |
197 | |
198 | static inline int sms_to_status(u32 is_demod_locked, u32 is_rf_locked) |
199 | { |
200 | if (is_demod_locked) |
201 | return FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | |
202 | FE_HAS_SYNC | FE_HAS_LOCK; |
203 | |
204 | if (is_rf_locked) |
205 | return FE_HAS_SIGNAL | FE_HAS_CARRIER; |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | static inline u32 sms_to_bw(u32 value) |
211 | { |
212 | return value * 1000000; |
213 | } |
214 | |
215 | #define convert_from_table(value, table, defval) ({ \ |
216 | u32 __ret; \ |
217 | if (value < ARRAY_SIZE(table)) \ |
218 | __ret = table[value]; \ |
219 | else \ |
220 | __ret = defval; \ |
221 | __ret; \ |
222 | }) |
223 | |
224 | #define sms_to_guard_interval(value) \ |
225 | convert_from_table(value, sms_to_guard_interval_table, \ |
226 | GUARD_INTERVAL_AUTO); |
227 | |
228 | #define sms_to_code_rate(value) \ |
229 | convert_from_table(value, sms_to_code_rate_table, \ |
230 | FEC_NONE); |
231 | |
232 | #define sms_to_hierarchy(value) \ |
233 | convert_from_table(value, sms_to_hierarchy_table, \ |
234 | FEC_NONE); |
235 | |
236 | #define sms_to_modulation(value) \ |
237 | convert_from_table(value, sms_to_modulation_table, \ |
238 | FEC_NONE); |
239 | |
240 | static void smsdvb_update_tx_params(struct smsdvb_client_t *client, |
241 | struct sms_tx_stats *p) |
242 | { |
243 | struct dvb_frontend *fe = &client->frontend; |
244 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
245 | |
246 | c->frequency = p->frequency; |
247 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: 0); |
248 | c->bandwidth_hz = sms_to_bw(value: p->bandwidth); |
249 | c->transmission_mode = sms_to_mode(mode: p->transmission_mode); |
250 | c->guard_interval = sms_to_guard_interval(p->guard_interval); |
251 | c->code_rate_HP = sms_to_code_rate(p->code_rate); |
252 | c->code_rate_LP = sms_to_code_rate(p->lp_code_rate); |
253 | c->hierarchy = sms_to_hierarchy(p->hierarchy); |
254 | c->modulation = sms_to_modulation(p->constellation); |
255 | } |
256 | |
257 | static void smsdvb_update_per_slices(struct smsdvb_client_t *client, |
258 | struct RECEPTION_STATISTICS_PER_SLICES_S *p) |
259 | { |
260 | struct dvb_frontend *fe = &client->frontend; |
261 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
262 | u64 tmp; |
263 | |
264 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: p->is_rf_locked); |
265 | c->modulation = sms_to_modulation(p->constellation); |
266 | |
267 | /* signal Strength, in DBm */ |
268 | c->strength.stat[0].uvalue = p->in_band_power * 1000; |
269 | |
270 | /* Carrier to noise ratio, in DB */ |
271 | c->cnr.stat[0].svalue = p->snr * 1000; |
272 | |
273 | /* PER/BER requires demod lock */ |
274 | if (!p->is_demod_locked) |
275 | return; |
276 | |
277 | /* TS PER */ |
278 | client->last_per = c->block_error.stat[0].uvalue; |
279 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
280 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; |
281 | c->block_error.stat[0].uvalue += p->ets_packets; |
282 | c->block_count.stat[0].uvalue += p->ets_packets + p->ts_packets; |
283 | |
284 | /* ber */ |
285 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
286 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; |
287 | c->post_bit_error.stat[0].uvalue += p->ber_error_count; |
288 | c->post_bit_count.stat[0].uvalue += p->ber_bit_count; |
289 | |
290 | /* Legacy PER/BER */ |
291 | tmp = p->ets_packets * 65535ULL; |
292 | if (p->ts_packets + p->ets_packets) |
293 | do_div(tmp, p->ts_packets + p->ets_packets); |
294 | client->legacy_per = tmp; |
295 | } |
296 | |
297 | static void smsdvb_update_dvb_stats(struct smsdvb_client_t *client, |
298 | struct sms_stats *p) |
299 | { |
300 | struct dvb_frontend *fe = &client->frontend; |
301 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
302 | |
303 | if (client->prt_dvb_stats) |
304 | client->prt_dvb_stats(client->debug_data, p); |
305 | |
306 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: p->is_rf_locked); |
307 | |
308 | /* Update DVB modulation parameters */ |
309 | c->frequency = p->frequency; |
310 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: 0); |
311 | c->bandwidth_hz = sms_to_bw(value: p->bandwidth); |
312 | c->transmission_mode = sms_to_mode(mode: p->transmission_mode); |
313 | c->guard_interval = sms_to_guard_interval(p->guard_interval); |
314 | c->code_rate_HP = sms_to_code_rate(p->code_rate); |
315 | c->code_rate_LP = sms_to_code_rate(p->lp_code_rate); |
316 | c->hierarchy = sms_to_hierarchy(p->hierarchy); |
317 | c->modulation = sms_to_modulation(p->constellation); |
318 | |
319 | /* update reception data */ |
320 | c->lna = p->is_external_lna_on ? 1 : 0; |
321 | |
322 | /* Carrier to noise ratio, in DB */ |
323 | c->cnr.stat[0].svalue = p->SNR * 1000; |
324 | |
325 | /* signal Strength, in DBm */ |
326 | c->strength.stat[0].uvalue = p->in_band_pwr * 1000; |
327 | |
328 | /* PER/BER requires demod lock */ |
329 | if (!p->is_demod_locked) |
330 | return; |
331 | |
332 | /* TS PER */ |
333 | client->last_per = c->block_error.stat[0].uvalue; |
334 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
335 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; |
336 | c->block_error.stat[0].uvalue += p->error_ts_packets; |
337 | c->block_count.stat[0].uvalue += p->total_ts_packets; |
338 | |
339 | /* ber */ |
340 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
341 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; |
342 | c->post_bit_error.stat[0].uvalue += p->ber_error_count; |
343 | c->post_bit_count.stat[0].uvalue += p->ber_bit_count; |
344 | |
345 | /* Legacy PER/BER */ |
346 | client->legacy_ber = p->ber; |
347 | }; |
348 | |
349 | static void smsdvb_update_isdbt_stats(struct smsdvb_client_t *client, |
350 | struct sms_isdbt_stats *p) |
351 | { |
352 | struct dvb_frontend *fe = &client->frontend; |
353 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
354 | struct sms_isdbt_layer_stats *lr; |
355 | int i, n_layers; |
356 | |
357 | if (client->prt_isdb_stats) |
358 | client->prt_isdb_stats(client->debug_data, p); |
359 | |
360 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: p->is_rf_locked); |
361 | |
362 | /* |
363 | * Firmware 2.1 seems to report only lock status and |
364 | * signal strength. The signal strength indicator is at the |
365 | * wrong field. |
366 | */ |
367 | if (p->statistics_type == 0) { |
368 | c->strength.stat[0].uvalue = ((s32)p->transmission_mode) * 1000; |
369 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
370 | return; |
371 | } |
372 | |
373 | /* Update ISDB-T transmission parameters */ |
374 | c->frequency = p->frequency; |
375 | c->bandwidth_hz = sms_to_bw(value: p->bandwidth); |
376 | c->transmission_mode = sms_to_isdbt_mode(mode: p->transmission_mode); |
377 | c->guard_interval = sms_to_isdbt_guard_interval(interval: p->guard_interval); |
378 | c->isdbt_partial_reception = p->partial_reception ? 1 : 0; |
379 | n_layers = p->num_of_layers; |
380 | if (n_layers < 1) |
381 | n_layers = 1; |
382 | if (n_layers > 3) |
383 | n_layers = 3; |
384 | c->isdbt_layer_enabled = 0; |
385 | |
386 | /* update reception data */ |
387 | c->lna = p->is_external_lna_on ? 1 : 0; |
388 | |
389 | /* Carrier to noise ratio, in DB */ |
390 | c->cnr.stat[0].svalue = p->SNR * 1000; |
391 | |
392 | /* signal Strength, in DBm */ |
393 | c->strength.stat[0].uvalue = p->in_band_pwr * 1000; |
394 | |
395 | /* PER/BER and per-layer stats require demod lock */ |
396 | if (!p->is_demod_locked) |
397 | return; |
398 | |
399 | client->last_per = c->block_error.stat[0].uvalue; |
400 | |
401 | /* Clears global counters, as the code below will sum it again */ |
402 | c->block_error.stat[0].uvalue = 0; |
403 | c->block_count.stat[0].uvalue = 0; |
404 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
405 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; |
406 | c->post_bit_error.stat[0].uvalue = 0; |
407 | c->post_bit_count.stat[0].uvalue = 0; |
408 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
409 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; |
410 | |
411 | for (i = 0; i < n_layers; i++) { |
412 | lr = &p->layer_info[i]; |
413 | |
414 | /* Update per-layer transmission parameters */ |
415 | if (lr->number_of_segments > 0 && lr->number_of_segments < 13) { |
416 | c->isdbt_layer_enabled |= 1 << i; |
417 | c->layer[i].segment_count = lr->number_of_segments; |
418 | } else { |
419 | continue; |
420 | } |
421 | c->layer[i].modulation = sms_to_modulation(lr->constellation); |
422 | c->layer[i].fec = sms_to_code_rate(lr->code_rate); |
423 | |
424 | /* Time interleaving */ |
425 | c->layer[i].interleaving = (u8)lr->ti_ldepth_i; |
426 | |
427 | /* TS PER */ |
428 | c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER; |
429 | c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER; |
430 | c->block_error.stat[i + 1].uvalue += lr->error_ts_packets; |
431 | c->block_count.stat[i + 1].uvalue += lr->total_ts_packets; |
432 | |
433 | /* Update global PER counter */ |
434 | c->block_error.stat[0].uvalue += lr->error_ts_packets; |
435 | c->block_count.stat[0].uvalue += lr->total_ts_packets; |
436 | |
437 | /* BER */ |
438 | c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER; |
439 | c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER; |
440 | c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count; |
441 | c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count; |
442 | |
443 | /* Update global BER counter */ |
444 | c->post_bit_error.stat[0].uvalue += lr->ber_error_count; |
445 | c->post_bit_count.stat[0].uvalue += lr->ber_bit_count; |
446 | } |
447 | } |
448 | |
449 | static void smsdvb_update_isdbt_stats_ex(struct smsdvb_client_t *client, |
450 | struct sms_isdbt_stats_ex *p) |
451 | { |
452 | struct dvb_frontend *fe = &client->frontend; |
453 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
454 | struct sms_isdbt_layer_stats *lr; |
455 | int i, n_layers; |
456 | |
457 | if (client->prt_isdb_stats_ex) |
458 | client->prt_isdb_stats_ex(client->debug_data, p); |
459 | |
460 | /* Update ISDB-T transmission parameters */ |
461 | c->frequency = p->frequency; |
462 | client->fe_status = sms_to_status(is_demod_locked: p->is_demod_locked, is_rf_locked: 0); |
463 | c->bandwidth_hz = sms_to_bw(value: p->bandwidth); |
464 | c->transmission_mode = sms_to_isdbt_mode(mode: p->transmission_mode); |
465 | c->guard_interval = sms_to_isdbt_guard_interval(interval: p->guard_interval); |
466 | c->isdbt_partial_reception = p->partial_reception ? 1 : 0; |
467 | n_layers = p->num_of_layers; |
468 | if (n_layers < 1) |
469 | n_layers = 1; |
470 | if (n_layers > 3) |
471 | n_layers = 3; |
472 | c->isdbt_layer_enabled = 0; |
473 | |
474 | /* update reception data */ |
475 | c->lna = p->is_external_lna_on ? 1 : 0; |
476 | |
477 | /* Carrier to noise ratio, in DB */ |
478 | c->cnr.stat[0].svalue = p->SNR * 1000; |
479 | |
480 | /* signal Strength, in DBm */ |
481 | c->strength.stat[0].uvalue = p->in_band_pwr * 1000; |
482 | |
483 | /* PER/BER and per-layer stats require demod lock */ |
484 | if (!p->is_demod_locked) |
485 | return; |
486 | |
487 | client->last_per = c->block_error.stat[0].uvalue; |
488 | |
489 | /* Clears global counters, as the code below will sum it again */ |
490 | c->block_error.stat[0].uvalue = 0; |
491 | c->block_count.stat[0].uvalue = 0; |
492 | c->block_error.stat[0].scale = FE_SCALE_COUNTER; |
493 | c->block_count.stat[0].scale = FE_SCALE_COUNTER; |
494 | c->post_bit_error.stat[0].uvalue = 0; |
495 | c->post_bit_count.stat[0].uvalue = 0; |
496 | c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; |
497 | c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; |
498 | |
499 | c->post_bit_error.len = n_layers + 1; |
500 | c->post_bit_count.len = n_layers + 1; |
501 | c->block_error.len = n_layers + 1; |
502 | c->block_count.len = n_layers + 1; |
503 | for (i = 0; i < n_layers; i++) { |
504 | lr = &p->layer_info[i]; |
505 | |
506 | /* Update per-layer transmission parameters */ |
507 | if (lr->number_of_segments > 0 && lr->number_of_segments < 13) { |
508 | c->isdbt_layer_enabled |= 1 << i; |
509 | c->layer[i].segment_count = lr->number_of_segments; |
510 | } else { |
511 | continue; |
512 | } |
513 | c->layer[i].modulation = sms_to_modulation(lr->constellation); |
514 | c->layer[i].fec = sms_to_code_rate(lr->code_rate); |
515 | |
516 | /* Time interleaving */ |
517 | c->layer[i].interleaving = (u8)lr->ti_ldepth_i; |
518 | |
519 | /* TS PER */ |
520 | c->block_error.stat[i + 1].scale = FE_SCALE_COUNTER; |
521 | c->block_count.stat[i + 1].scale = FE_SCALE_COUNTER; |
522 | c->block_error.stat[i + 1].uvalue += lr->error_ts_packets; |
523 | c->block_count.stat[i + 1].uvalue += lr->total_ts_packets; |
524 | |
525 | /* Update global PER counter */ |
526 | c->block_error.stat[0].uvalue += lr->error_ts_packets; |
527 | c->block_count.stat[0].uvalue += lr->total_ts_packets; |
528 | |
529 | /* ber */ |
530 | c->post_bit_error.stat[i + 1].scale = FE_SCALE_COUNTER; |
531 | c->post_bit_count.stat[i + 1].scale = FE_SCALE_COUNTER; |
532 | c->post_bit_error.stat[i + 1].uvalue += lr->ber_error_count; |
533 | c->post_bit_count.stat[i + 1].uvalue += lr->ber_bit_count; |
534 | |
535 | /* Update global ber counter */ |
536 | c->post_bit_error.stat[0].uvalue += lr->ber_error_count; |
537 | c->post_bit_count.stat[0].uvalue += lr->ber_bit_count; |
538 | } |
539 | } |
540 | |
541 | static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) |
542 | { |
543 | struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; |
544 | struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) (((u8 *) cb->p) |
545 | + cb->offset); |
546 | void *p = phdr + 1; |
547 | struct dvb_frontend *fe = &client->frontend; |
548 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
549 | bool is_status_update = false; |
550 | |
551 | switch (phdr->msg_type) { |
552 | case MSG_SMS_DVBT_BDA_DATA: |
553 | /* |
554 | * Only feed data to dvb demux if are there any feed listening |
555 | * to it and if the device has tuned |
556 | */ |
557 | if (client->feed_users && client->has_tuned) |
558 | dvb_dmx_swfilter(demux: &client->demux, buf: p, |
559 | count: cb->size - sizeof(struct sms_msg_hdr)); |
560 | break; |
561 | |
562 | case MSG_SMS_RF_TUNE_RES: |
563 | case MSG_SMS_ISDBT_TUNE_RES: |
564 | complete(&client->tune_done); |
565 | break; |
566 | |
567 | case MSG_SMS_SIGNAL_DETECTED_IND: |
568 | client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | |
569 | FE_HAS_VITERBI | FE_HAS_SYNC | |
570 | FE_HAS_LOCK; |
571 | |
572 | is_status_update = true; |
573 | break; |
574 | |
575 | case MSG_SMS_NO_SIGNAL_IND: |
576 | client->fe_status = 0; |
577 | |
578 | is_status_update = true; |
579 | break; |
580 | |
581 | case MSG_SMS_TRANSMISSION_IND: |
582 | smsdvb_update_tx_params(client, p); |
583 | |
584 | is_status_update = true; |
585 | break; |
586 | |
587 | case MSG_SMS_HO_PER_SLICES_IND: |
588 | smsdvb_update_per_slices(client, p); |
589 | |
590 | is_status_update = true; |
591 | break; |
592 | |
593 | case MSG_SMS_GET_STATISTICS_RES: |
594 | switch (smscore_get_device_mode(coredev: client->coredev)) { |
595 | case DEVICE_MODE_ISDBT: |
596 | case DEVICE_MODE_ISDBT_BDA: |
597 | smsdvb_update_isdbt_stats(client, p); |
598 | break; |
599 | default: |
600 | /* Skip sms_msg_statistics_info:request_result field */ |
601 | smsdvb_update_dvb_stats(client, p: p + sizeof(u32)); |
602 | } |
603 | |
604 | is_status_update = true; |
605 | break; |
606 | |
607 | /* Only for ISDB-T */ |
608 | case MSG_SMS_GET_STATISTICS_EX_RES: |
609 | /* Skip sms_msg_statistics_info:request_result field? */ |
610 | smsdvb_update_isdbt_stats_ex(client, p: p + sizeof(u32)); |
611 | is_status_update = true; |
612 | break; |
613 | default: |
614 | pr_debug("message not handled\n" ); |
615 | } |
616 | smscore_putbuffer(coredev: client->coredev, cb); |
617 | |
618 | if (is_status_update) { |
619 | if (client->fe_status & FE_HAS_LOCK) { |
620 | sms_board_dvb3_event(client, event: DVB3_EVENT_FE_LOCK); |
621 | if (client->last_per == c->block_error.stat[0].uvalue) |
622 | sms_board_dvb3_event(client, event: DVB3_EVENT_UNC_OK); |
623 | else |
624 | sms_board_dvb3_event(client, event: DVB3_EVENT_UNC_ERR); |
625 | client->has_tuned = true; |
626 | } else { |
627 | smsdvb_stats_not_ready(fe); |
628 | client->has_tuned = false; |
629 | sms_board_dvb3_event(client, event: DVB3_EVENT_FE_UNLOCK); |
630 | } |
631 | complete(&client->stats_done); |
632 | } |
633 | |
634 | return 0; |
635 | } |
636 | |
637 | static void smsdvb_media_device_unregister(struct smsdvb_client_t *client) |
638 | { |
639 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
640 | struct smscore_device_t *coredev = client->coredev; |
641 | |
642 | if (!coredev->media_dev) |
643 | return; |
644 | media_device_unregister(mdev: coredev->media_dev); |
645 | media_device_cleanup(mdev: coredev->media_dev); |
646 | kfree(objp: coredev->media_dev); |
647 | coredev->media_dev = NULL; |
648 | #endif |
649 | } |
650 | |
651 | static void smsdvb_unregister_client(struct smsdvb_client_t *client) |
652 | { |
653 | /* must be called under clientslock */ |
654 | |
655 | list_del(entry: &client->entry); |
656 | |
657 | smsdvb_debugfs_release(client); |
658 | smscore_unregister_client(client: client->smsclient); |
659 | dvb_unregister_frontend(fe: &client->frontend); |
660 | dvb_dmxdev_release(dmxdev: &client->dmxdev); |
661 | dvb_dmx_release(demux: &client->demux); |
662 | smsdvb_media_device_unregister(client); |
663 | dvb_unregister_adapter(adap: &client->adapter); |
664 | kfree(objp: client); |
665 | } |
666 | |
667 | static void smsdvb_onremove(void *context) |
668 | { |
669 | mutex_lock(&g_smsdvb_clientslock); |
670 | |
671 | smsdvb_unregister_client(client: (struct smsdvb_client_t *) context); |
672 | |
673 | mutex_unlock(lock: &g_smsdvb_clientslock); |
674 | } |
675 | |
676 | static int smsdvb_start_feed(struct dvb_demux_feed *feed) |
677 | { |
678 | struct smsdvb_client_t *client = |
679 | container_of(feed->demux, struct smsdvb_client_t, demux); |
680 | struct sms_msg_data pid_msg; |
681 | |
682 | pr_debug("add pid %d(%x)\n" , |
683 | feed->pid, feed->pid); |
684 | |
685 | client->feed_users++; |
686 | |
687 | pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; |
688 | pid_msg.x_msg_header.msg_dst_id = HIF_TASK; |
689 | pid_msg.x_msg_header.msg_flags = 0; |
690 | pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ; |
691 | pid_msg.x_msg_header.msg_length = sizeof(pid_msg); |
692 | pid_msg.msg_data[0] = feed->pid; |
693 | |
694 | return smsclient_sendrequest(client: client->smsclient, |
695 | buffer: &pid_msg, size: sizeof(pid_msg)); |
696 | } |
697 | |
698 | static int smsdvb_stop_feed(struct dvb_demux_feed *feed) |
699 | { |
700 | struct smsdvb_client_t *client = |
701 | container_of(feed->demux, struct smsdvb_client_t, demux); |
702 | struct sms_msg_data pid_msg; |
703 | |
704 | pr_debug("remove pid %d(%x)\n" , |
705 | feed->pid, feed->pid); |
706 | |
707 | client->feed_users--; |
708 | |
709 | pid_msg.x_msg_header.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; |
710 | pid_msg.x_msg_header.msg_dst_id = HIF_TASK; |
711 | pid_msg.x_msg_header.msg_flags = 0; |
712 | pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ; |
713 | pid_msg.x_msg_header.msg_length = sizeof(pid_msg); |
714 | pid_msg.msg_data[0] = feed->pid; |
715 | |
716 | return smsclient_sendrequest(client: client->smsclient, |
717 | buffer: &pid_msg, size: sizeof(pid_msg)); |
718 | } |
719 | |
720 | static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, |
721 | void *buffer, size_t size, |
722 | struct completion *completion) |
723 | { |
724 | int rc; |
725 | |
726 | rc = smsclient_sendrequest(client: client->smsclient, buffer, size); |
727 | if (rc < 0) |
728 | return rc; |
729 | |
730 | return wait_for_completion_timeout(x: completion, |
731 | timeout: msecs_to_jiffies(m: 2000)) ? |
732 | 0 : -ETIME; |
733 | } |
734 | |
735 | static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) |
736 | { |
737 | int rc; |
738 | struct sms_msg_hdr msg; |
739 | |
740 | /* Don't request stats too fast */ |
741 | if (client->get_stats_jiffies && |
742 | (!time_after(jiffies, client->get_stats_jiffies))) |
743 | return 0; |
744 | client->get_stats_jiffies = jiffies + msecs_to_jiffies(m: 100); |
745 | |
746 | msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; |
747 | msg.msg_dst_id = HIF_TASK; |
748 | msg.msg_flags = 0; |
749 | msg.msg_length = sizeof(msg); |
750 | |
751 | switch (smscore_get_device_mode(coredev: client->coredev)) { |
752 | case DEVICE_MODE_ISDBT: |
753 | case DEVICE_MODE_ISDBT_BDA: |
754 | /* |
755 | * Check for firmware version, to avoid breaking for old cards |
756 | */ |
757 | if (client->coredev->fw_version >= 0x800) |
758 | msg.msg_type = MSG_SMS_GET_STATISTICS_EX_REQ; |
759 | else |
760 | msg.msg_type = MSG_SMS_GET_STATISTICS_REQ; |
761 | break; |
762 | default: |
763 | msg.msg_type = MSG_SMS_GET_STATISTICS_REQ; |
764 | } |
765 | |
766 | rc = smsdvb_sendrequest_and_wait(client, buffer: &msg, size: sizeof(msg), |
767 | completion: &client->stats_done); |
768 | |
769 | return rc; |
770 | } |
771 | |
772 | static inline int led_feedback(struct smsdvb_client_t *client) |
773 | { |
774 | if (!(client->fe_status & FE_HAS_LOCK)) |
775 | return sms_board_led_feedback(coredev: client->coredev, SMS_LED_OFF); |
776 | |
777 | return sms_board_led_feedback(coredev: client->coredev, |
778 | led: (client->legacy_ber == 0) ? |
779 | SMS_LED_HI : SMS_LED_LO); |
780 | } |
781 | |
782 | static int smsdvb_read_status(struct dvb_frontend *fe, enum fe_status *stat) |
783 | { |
784 | int rc; |
785 | struct smsdvb_client_t *client; |
786 | client = container_of(fe, struct smsdvb_client_t, frontend); |
787 | |
788 | rc = smsdvb_send_statistics_request(client); |
789 | |
790 | *stat = client->fe_status; |
791 | |
792 | led_feedback(client); |
793 | |
794 | return rc; |
795 | } |
796 | |
797 | static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) |
798 | { |
799 | int rc; |
800 | struct smsdvb_client_t *client; |
801 | |
802 | client = container_of(fe, struct smsdvb_client_t, frontend); |
803 | |
804 | rc = smsdvb_send_statistics_request(client); |
805 | |
806 | *ber = client->legacy_ber; |
807 | |
808 | led_feedback(client); |
809 | |
810 | return rc; |
811 | } |
812 | |
813 | static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) |
814 | { |
815 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
816 | int rc; |
817 | s32 power = (s32) c->strength.stat[0].uvalue; |
818 | struct smsdvb_client_t *client; |
819 | |
820 | client = container_of(fe, struct smsdvb_client_t, frontend); |
821 | |
822 | rc = smsdvb_send_statistics_request(client); |
823 | |
824 | if (power < -95) |
825 | *strength = 0; |
826 | else if (power > -29) |
827 | *strength = 65535; |
828 | else |
829 | *strength = (power + 95) * 65535 / 66; |
830 | |
831 | led_feedback(client); |
832 | |
833 | return rc; |
834 | } |
835 | |
836 | static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) |
837 | { |
838 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
839 | int rc; |
840 | struct smsdvb_client_t *client; |
841 | |
842 | client = container_of(fe, struct smsdvb_client_t, frontend); |
843 | |
844 | rc = smsdvb_send_statistics_request(client); |
845 | |
846 | /* Preferred scale for SNR with legacy API: 0.1 dB */ |
847 | *snr = ((u32)c->cnr.stat[0].svalue) / 100; |
848 | |
849 | led_feedback(client); |
850 | |
851 | return rc; |
852 | } |
853 | |
854 | static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) |
855 | { |
856 | int rc; |
857 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
858 | struct smsdvb_client_t *client; |
859 | |
860 | client = container_of(fe, struct smsdvb_client_t, frontend); |
861 | |
862 | rc = smsdvb_send_statistics_request(client); |
863 | |
864 | *ucblocks = c->block_error.stat[0].uvalue; |
865 | |
866 | led_feedback(client); |
867 | |
868 | return rc; |
869 | } |
870 | |
871 | static int smsdvb_get_tune_settings(struct dvb_frontend *fe, |
872 | struct dvb_frontend_tune_settings *tune) |
873 | { |
874 | pr_debug("\n" ); |
875 | |
876 | tune->min_delay_ms = 400; |
877 | tune->step_size = 250000; |
878 | tune->max_drift = 0; |
879 | return 0; |
880 | } |
881 | |
882 | static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe) |
883 | { |
884 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
885 | struct smsdvb_client_t *client = |
886 | container_of(fe, struct smsdvb_client_t, frontend); |
887 | |
888 | struct { |
889 | struct sms_msg_hdr msg; |
890 | u32 Data[3]; |
891 | } msg; |
892 | |
893 | int ret; |
894 | |
895 | client->fe_status = 0; |
896 | client->event_fe_state = -1; |
897 | client->event_unc_state = -1; |
898 | fe->dtv_property_cache.delivery_system = SYS_DVBT; |
899 | |
900 | msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; |
901 | msg.msg.msg_dst_id = HIF_TASK; |
902 | msg.msg.msg_flags = 0; |
903 | msg.msg.msg_type = MSG_SMS_RF_TUNE_REQ; |
904 | msg.msg.msg_length = sizeof(msg); |
905 | msg.Data[0] = c->frequency; |
906 | msg.Data[2] = 12000000; |
907 | |
908 | pr_debug("%s: freq %d band %d\n" , __func__, c->frequency, |
909 | c->bandwidth_hz); |
910 | |
911 | switch (c->bandwidth_hz / 1000000) { |
912 | case 8: |
913 | msg.Data[1] = BW_8_MHZ; |
914 | break; |
915 | case 7: |
916 | msg.Data[1] = BW_7_MHZ; |
917 | break; |
918 | case 6: |
919 | msg.Data[1] = BW_6_MHZ; |
920 | break; |
921 | case 0: |
922 | return -EOPNOTSUPP; |
923 | default: |
924 | return -EINVAL; |
925 | } |
926 | /* Disable LNA, if any. An error is returned if no LNA is present */ |
927 | ret = sms_board_lna_control(coredev: client->coredev, onoff: 0); |
928 | if (ret == 0) { |
929 | enum fe_status status; |
930 | |
931 | /* tune with LNA off at first */ |
932 | ret = smsdvb_sendrequest_and_wait(client, buffer: &msg, size: sizeof(msg), |
933 | completion: &client->tune_done); |
934 | |
935 | smsdvb_read_status(fe, stat: &status); |
936 | |
937 | if (status & FE_HAS_LOCK) |
938 | return ret; |
939 | |
940 | /* previous tune didn't lock - enable LNA and tune again */ |
941 | sms_board_lna_control(coredev: client->coredev, onoff: 1); |
942 | } |
943 | |
944 | return smsdvb_sendrequest_and_wait(client, buffer: &msg, size: sizeof(msg), |
945 | completion: &client->tune_done); |
946 | } |
947 | |
948 | static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe) |
949 | { |
950 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
951 | struct smsdvb_client_t *client = |
952 | container_of(fe, struct smsdvb_client_t, frontend); |
953 | int board_id = smscore_get_board_id(core: client->coredev); |
954 | struct sms_board *board = sms_get_board(id: board_id); |
955 | enum sms_device_type_st type = board->type; |
956 | int ret; |
957 | |
958 | struct { |
959 | struct sms_msg_hdr msg; |
960 | u32 Data[4]; |
961 | } msg; |
962 | |
963 | fe->dtv_property_cache.delivery_system = SYS_ISDBT; |
964 | |
965 | msg.msg.msg_src_id = DVBT_BDA_CONTROL_MSG_ID; |
966 | msg.msg.msg_dst_id = HIF_TASK; |
967 | msg.msg.msg_flags = 0; |
968 | msg.msg.msg_type = MSG_SMS_ISDBT_TUNE_REQ; |
969 | msg.msg.msg_length = sizeof(msg); |
970 | |
971 | if (c->isdbt_sb_segment_idx == -1) |
972 | c->isdbt_sb_segment_idx = 0; |
973 | |
974 | if (!c->isdbt_layer_enabled) |
975 | c->isdbt_layer_enabled = 7; |
976 | |
977 | msg.Data[0] = c->frequency; |
978 | msg.Data[1] = BW_ISDBT_1SEG; |
979 | msg.Data[2] = 12000000; |
980 | msg.Data[3] = c->isdbt_sb_segment_idx; |
981 | |
982 | if (c->isdbt_partial_reception) { |
983 | if ((type == SMS_PELE || type == SMS_RIO) && |
984 | c->isdbt_sb_segment_count > 3) |
985 | msg.Data[1] = BW_ISDBT_13SEG; |
986 | else if (c->isdbt_sb_segment_count > 1) |
987 | msg.Data[1] = BW_ISDBT_3SEG; |
988 | } else if (type == SMS_PELE || type == SMS_RIO) |
989 | msg.Data[1] = BW_ISDBT_13SEG; |
990 | |
991 | c->bandwidth_hz = 6000000; |
992 | |
993 | pr_debug("freq %d segwidth %d segindex %d\n" , |
994 | c->frequency, c->isdbt_sb_segment_count, |
995 | c->isdbt_sb_segment_idx); |
996 | |
997 | /* Disable LNA, if any. An error is returned if no LNA is present */ |
998 | ret = sms_board_lna_control(coredev: client->coredev, onoff: 0); |
999 | if (ret == 0) { |
1000 | enum fe_status status; |
1001 | |
1002 | /* tune with LNA off at first */ |
1003 | ret = smsdvb_sendrequest_and_wait(client, buffer: &msg, size: sizeof(msg), |
1004 | completion: &client->tune_done); |
1005 | |
1006 | smsdvb_read_status(fe, stat: &status); |
1007 | |
1008 | if (status & FE_HAS_LOCK) |
1009 | return ret; |
1010 | |
1011 | /* previous tune didn't lock - enable LNA and tune again */ |
1012 | sms_board_lna_control(coredev: client->coredev, onoff: 1); |
1013 | } |
1014 | return smsdvb_sendrequest_and_wait(client, buffer: &msg, size: sizeof(msg), |
1015 | completion: &client->tune_done); |
1016 | } |
1017 | |
1018 | static int smsdvb_set_frontend(struct dvb_frontend *fe) |
1019 | { |
1020 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
1021 | struct smsdvb_client_t *client = |
1022 | container_of(fe, struct smsdvb_client_t, frontend); |
1023 | struct smscore_device_t *coredev = client->coredev; |
1024 | |
1025 | smsdvb_stats_not_ready(fe); |
1026 | c->strength.stat[0].uvalue = 0; |
1027 | c->cnr.stat[0].uvalue = 0; |
1028 | |
1029 | client->has_tuned = false; |
1030 | |
1031 | switch (smscore_get_device_mode(coredev)) { |
1032 | case DEVICE_MODE_DVBT: |
1033 | case DEVICE_MODE_DVBT_BDA: |
1034 | return smsdvb_dvbt_set_frontend(fe); |
1035 | case DEVICE_MODE_ISDBT: |
1036 | case DEVICE_MODE_ISDBT_BDA: |
1037 | return smsdvb_isdbt_set_frontend(fe); |
1038 | default: |
1039 | return -EINVAL; |
1040 | } |
1041 | } |
1042 | |
1043 | static int smsdvb_init(struct dvb_frontend *fe) |
1044 | { |
1045 | struct smsdvb_client_t *client = |
1046 | container_of(fe, struct smsdvb_client_t, frontend); |
1047 | |
1048 | sms_board_power(coredev: client->coredev, onoff: 1); |
1049 | |
1050 | sms_board_dvb3_event(client, event: DVB3_EVENT_INIT); |
1051 | return 0; |
1052 | } |
1053 | |
1054 | static int smsdvb_sleep(struct dvb_frontend *fe) |
1055 | { |
1056 | struct smsdvb_client_t *client = |
1057 | container_of(fe, struct smsdvb_client_t, frontend); |
1058 | |
1059 | sms_board_led_feedback(coredev: client->coredev, SMS_LED_OFF); |
1060 | sms_board_power(coredev: client->coredev, onoff: 0); |
1061 | |
1062 | sms_board_dvb3_event(client, event: DVB3_EVENT_SLEEP); |
1063 | |
1064 | return 0; |
1065 | } |
1066 | |
1067 | static void smsdvb_release(struct dvb_frontend *fe) |
1068 | { |
1069 | /* do nothing */ |
1070 | } |
1071 | |
1072 | static const struct dvb_frontend_ops smsdvb_fe_ops = { |
1073 | .info = { |
1074 | .name = "Siano Mobile Digital MDTV Receiver" , |
1075 | .frequency_min_hz = 44250 * kHz, |
1076 | .frequency_max_hz = 867250 * kHz, |
1077 | .frequency_stepsize_hz = 250 * kHz, |
1078 | .caps = FE_CAN_INVERSION_AUTO | |
1079 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
1080 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | |
1081 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | |
1082 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | |
1083 | FE_CAN_GUARD_INTERVAL_AUTO | |
1084 | FE_CAN_RECOVER | |
1085 | FE_CAN_HIERARCHY_AUTO, |
1086 | }, |
1087 | |
1088 | .release = smsdvb_release, |
1089 | |
1090 | .set_frontend = smsdvb_set_frontend, |
1091 | .get_tune_settings = smsdvb_get_tune_settings, |
1092 | |
1093 | .read_status = smsdvb_read_status, |
1094 | .read_ber = smsdvb_read_ber, |
1095 | .read_signal_strength = smsdvb_read_signal_strength, |
1096 | .read_snr = smsdvb_read_snr, |
1097 | .read_ucblocks = smsdvb_read_ucblocks, |
1098 | |
1099 | .init = smsdvb_init, |
1100 | .sleep = smsdvb_sleep, |
1101 | }; |
1102 | |
1103 | static int smsdvb_hotplug(struct smscore_device_t *coredev, |
1104 | struct device *device, int arrival) |
1105 | { |
1106 | struct smsclient_params_t params; |
1107 | struct smsdvb_client_t *client; |
1108 | int rc; |
1109 | |
1110 | /* device removal handled by onremove callback */ |
1111 | if (!arrival) |
1112 | return 0; |
1113 | client = kzalloc(size: sizeof(struct smsdvb_client_t), GFP_KERNEL); |
1114 | if (!client) |
1115 | return -ENOMEM; |
1116 | |
1117 | /* register dvb adapter */ |
1118 | rc = dvb_register_adapter(adap: &client->adapter, |
1119 | name: sms_get_board( |
1120 | id: smscore_get_board_id(core: coredev))->name, |
1121 | THIS_MODULE, device, adapter_nums: adapter_nr); |
1122 | if (rc < 0) { |
1123 | pr_err("dvb_register_adapter() failed %d\n" , rc); |
1124 | goto adapter_error; |
1125 | } |
1126 | dvb_register_media_controller(adap: &client->adapter, mdev: coredev->media_dev); |
1127 | |
1128 | /* init dvb demux */ |
1129 | client->demux.dmx.capabilities = DMX_TS_FILTERING; |
1130 | client->demux.filternum = 32; /* todo: nova ??? */ |
1131 | client->demux.feednum = 32; |
1132 | client->demux.start_feed = smsdvb_start_feed; |
1133 | client->demux.stop_feed = smsdvb_stop_feed; |
1134 | |
1135 | rc = dvb_dmx_init(demux: &client->demux); |
1136 | if (rc < 0) { |
1137 | pr_err("dvb_dmx_init failed %d\n" , rc); |
1138 | goto dvbdmx_error; |
1139 | } |
1140 | |
1141 | /* init dmxdev */ |
1142 | client->dmxdev.filternum = 32; |
1143 | client->dmxdev.demux = &client->demux.dmx; |
1144 | client->dmxdev.capabilities = 0; |
1145 | |
1146 | rc = dvb_dmxdev_init(dmxdev: &client->dmxdev, adap: &client->adapter); |
1147 | if (rc < 0) { |
1148 | pr_err("dvb_dmxdev_init failed %d\n" , rc); |
1149 | goto dmxdev_error; |
1150 | } |
1151 | |
1152 | /* init and register frontend */ |
1153 | memcpy(&client->frontend.ops, &smsdvb_fe_ops, |
1154 | sizeof(struct dvb_frontend_ops)); |
1155 | |
1156 | switch (smscore_get_device_mode(coredev)) { |
1157 | case DEVICE_MODE_DVBT: |
1158 | case DEVICE_MODE_DVBT_BDA: |
1159 | client->frontend.ops.delsys[0] = SYS_DVBT; |
1160 | break; |
1161 | case DEVICE_MODE_ISDBT: |
1162 | case DEVICE_MODE_ISDBT_BDA: |
1163 | client->frontend.ops.delsys[0] = SYS_ISDBT; |
1164 | break; |
1165 | } |
1166 | |
1167 | rc = dvb_register_frontend(dvb: &client->adapter, fe: &client->frontend); |
1168 | if (rc < 0) { |
1169 | pr_err("frontend registration failed %d\n" , rc); |
1170 | goto frontend_error; |
1171 | } |
1172 | |
1173 | params.initial_id = 1; |
1174 | params.data_type = MSG_SMS_DVBT_BDA_DATA; |
1175 | params.onresponse_handler = smsdvb_onresponse; |
1176 | params.onremove_handler = smsdvb_onremove; |
1177 | params.context = client; |
1178 | |
1179 | rc = smscore_register_client(coredev, params: ¶ms, client: &client->smsclient); |
1180 | if (rc < 0) { |
1181 | pr_err("smscore_register_client() failed %d\n" , rc); |
1182 | goto client_error; |
1183 | } |
1184 | |
1185 | client->coredev = coredev; |
1186 | |
1187 | init_completion(x: &client->tune_done); |
1188 | init_completion(x: &client->stats_done); |
1189 | |
1190 | mutex_lock(&g_smsdvb_clientslock); |
1191 | |
1192 | list_add(new: &client->entry, head: &g_smsdvb_clients); |
1193 | |
1194 | mutex_unlock(lock: &g_smsdvb_clientslock); |
1195 | |
1196 | client->event_fe_state = -1; |
1197 | client->event_unc_state = -1; |
1198 | sms_board_dvb3_event(client, event: DVB3_EVENT_HOTPLUG); |
1199 | |
1200 | sms_board_setup(coredev); |
1201 | |
1202 | if (smsdvb_debugfs_create(client) < 0) |
1203 | pr_info("failed to create debugfs node\n" ); |
1204 | |
1205 | rc = dvb_create_media_graph(adap: &client->adapter, create_rf_connector: true); |
1206 | if (rc < 0) { |
1207 | pr_err("dvb_create_media_graph failed %d\n" , rc); |
1208 | goto media_graph_error; |
1209 | } |
1210 | |
1211 | pr_info("DVB interface registered.\n" ); |
1212 | return 0; |
1213 | |
1214 | media_graph_error: |
1215 | mutex_lock(&g_smsdvb_clientslock); |
1216 | list_del(entry: &client->entry); |
1217 | mutex_unlock(lock: &g_smsdvb_clientslock); |
1218 | |
1219 | smsdvb_debugfs_release(client); |
1220 | |
1221 | client_error: |
1222 | dvb_unregister_frontend(fe: &client->frontend); |
1223 | |
1224 | frontend_error: |
1225 | dvb_dmxdev_release(dmxdev: &client->dmxdev); |
1226 | |
1227 | dmxdev_error: |
1228 | dvb_dmx_release(demux: &client->demux); |
1229 | |
1230 | dvbdmx_error: |
1231 | smsdvb_media_device_unregister(client); |
1232 | dvb_unregister_adapter(adap: &client->adapter); |
1233 | |
1234 | adapter_error: |
1235 | kfree(objp: client); |
1236 | return rc; |
1237 | } |
1238 | |
1239 | static int __init smsdvb_module_init(void) |
1240 | { |
1241 | int rc; |
1242 | |
1243 | smsdvb_debugfs_register(); |
1244 | |
1245 | rc = smscore_register_hotplug(hotplug: smsdvb_hotplug); |
1246 | |
1247 | pr_debug("\n" ); |
1248 | |
1249 | return rc; |
1250 | } |
1251 | |
1252 | static void __exit smsdvb_module_exit(void) |
1253 | { |
1254 | smscore_unregister_hotplug(hotplug: smsdvb_hotplug); |
1255 | |
1256 | mutex_lock(&g_smsdvb_clientslock); |
1257 | |
1258 | while (!list_empty(head: &g_smsdvb_clients)) |
1259 | smsdvb_unregister_client(client: (struct smsdvb_client_t *)g_smsdvb_clients.next); |
1260 | |
1261 | smsdvb_debugfs_unregister(); |
1262 | |
1263 | mutex_unlock(lock: &g_smsdvb_clientslock); |
1264 | } |
1265 | |
1266 | module_init(smsdvb_module_init); |
1267 | module_exit(smsdvb_module_exit); |
1268 | |
1269 | MODULE_DESCRIPTION("SMS DVB subsystem adaptation module" ); |
1270 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. <uris@siano-ms.com>" ); |
1271 | MODULE_LICENSE("GPL" ); |
1272 | |