1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * The Virtual DTV test driver serves as a reference DVB driver and helps |
4 | * validate the existing APIs in the media subsystem. It can also aid |
5 | * developers working on userspace applications. |
6 | * |
7 | * When this module is loaded, it will attempt to modprobe 'dvb_vidtv_tuner' |
8 | * and 'dvb_vidtv_demod'. |
9 | * |
10 | * Copyright (C) 2020 Daniel W. S. Almeida |
11 | */ |
12 | |
13 | #include <linux/dev_printk.h> |
14 | #include <linux/moduleparam.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/time.h> |
18 | #include <linux/types.h> |
19 | #include <linux/workqueue.h> |
20 | #include <media/dvbdev.h> |
21 | #include <media/media-device.h> |
22 | |
23 | #include "vidtv_bridge.h" |
24 | #include "vidtv_common.h" |
25 | #include "vidtv_demod.h" |
26 | #include "vidtv_mux.h" |
27 | #include "vidtv_ts.h" |
28 | #include "vidtv_tuner.h" |
29 | |
30 | #define MUX_BUF_MIN_SZ 90164 |
31 | #define MUX_BUF_MAX_SZ (MUX_BUF_MIN_SZ * 10) |
32 | #define TUNER_DEFAULT_ADDR 0x68 |
33 | #define DEMOD_DEFAULT_ADDR 0x60 |
34 | #define VIDTV_DEFAULT_NETWORK_ID 0xff44 |
35 | #define VIDTV_DEFAULT_NETWORK_NAME "LinuxTV.org" |
36 | #define VIDTV_DEFAULT_TS_ID 0x4081 |
37 | |
38 | /* |
39 | * The LNBf fake parameters here are the ranges used by an |
40 | * Universal (extended) European LNBf, which is likely the most common LNBf |
41 | * found on Satellite digital TV system nowadays. |
42 | */ |
43 | #define LNB_CUT_FREQUENCY 11700000 /* high IF frequency */ |
44 | #define LNB_LOW_FREQ 9750000 /* low IF frequency */ |
45 | #define LNB_HIGH_FREQ 10600000 /* transition frequency */ |
46 | |
47 | static unsigned int drop_tslock_prob_on_low_snr; |
48 | module_param(drop_tslock_prob_on_low_snr, uint, 0444); |
49 | MODULE_PARM_DESC(drop_tslock_prob_on_low_snr, |
50 | "Probability of losing the TS lock if the signal quality is bad" ); |
51 | |
52 | static unsigned int recover_tslock_prob_on_good_snr; |
53 | module_param(recover_tslock_prob_on_good_snr, uint, 0444); |
54 | MODULE_PARM_DESC(recover_tslock_prob_on_good_snr, |
55 | "Probability recovering the TS lock when the signal improves" ); |
56 | |
57 | static unsigned int mock_power_up_delay_msec; |
58 | module_param(mock_power_up_delay_msec, uint, 0444); |
59 | MODULE_PARM_DESC(mock_power_up_delay_msec, "Simulate a power up delay" ); |
60 | |
61 | static unsigned int mock_tune_delay_msec; |
62 | module_param(mock_tune_delay_msec, uint, 0444); |
63 | MODULE_PARM_DESC(mock_tune_delay_msec, "Simulate a tune delay" ); |
64 | |
65 | static unsigned int vidtv_valid_dvb_t_freqs[NUM_VALID_TUNER_FREQS] = { |
66 | 474000000 |
67 | }; |
68 | |
69 | module_param_array(vidtv_valid_dvb_t_freqs, uint, NULL, 0444); |
70 | MODULE_PARM_DESC(vidtv_valid_dvb_t_freqs, |
71 | "Valid DVB-T frequencies to simulate, in Hz" ); |
72 | |
73 | static unsigned int vidtv_valid_dvb_c_freqs[NUM_VALID_TUNER_FREQS] = { |
74 | 474000000 |
75 | }; |
76 | |
77 | module_param_array(vidtv_valid_dvb_c_freqs, uint, NULL, 0444); |
78 | MODULE_PARM_DESC(vidtv_valid_dvb_c_freqs, |
79 | "Valid DVB-C frequencies to simulate, in Hz" ); |
80 | |
81 | static unsigned int vidtv_valid_dvb_s_freqs[NUM_VALID_TUNER_FREQS] = { |
82 | 11362000 |
83 | }; |
84 | module_param_array(vidtv_valid_dvb_s_freqs, uint, NULL, 0444); |
85 | MODULE_PARM_DESC(vidtv_valid_dvb_s_freqs, |
86 | "Valid DVB-S/S2 frequencies to simulate at Ku-Band, in kHz" ); |
87 | |
88 | static unsigned int max_frequency_shift_hz; |
89 | module_param(max_frequency_shift_hz, uint, 0444); |
90 | MODULE_PARM_DESC(max_frequency_shift_hz, |
91 | "Maximum shift in HZ allowed when tuning in a channel" ); |
92 | |
93 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums); |
94 | |
95 | /* |
96 | * Influences the signal acquisition time. See ISO/IEC 13818-1 : 2000. p. 113. |
97 | */ |
98 | static unsigned int si_period_msec = 40; |
99 | module_param(si_period_msec, uint, 0444); |
100 | MODULE_PARM_DESC(si_period_msec, "How often to send SI packets. Default: 40ms" ); |
101 | |
102 | static unsigned int pcr_period_msec = 40; |
103 | module_param(pcr_period_msec, uint, 0444); |
104 | MODULE_PARM_DESC(pcr_period_msec, |
105 | "How often to send PCR packets. Default: 40ms" ); |
106 | |
107 | static unsigned int mux_rate_kbytes_sec = 4096; |
108 | module_param(mux_rate_kbytes_sec, uint, 0444); |
109 | MODULE_PARM_DESC(mux_rate_kbytes_sec, "Mux rate: will pad stream if below" ); |
110 | |
111 | static unsigned int pcr_pid = 0x200; |
112 | module_param(pcr_pid, uint, 0444); |
113 | MODULE_PARM_DESC(pcr_pid, "PCR PID for all channels: defaults to 0x200" ); |
114 | |
115 | static unsigned int mux_buf_sz_pkts; |
116 | module_param(mux_buf_sz_pkts, uint, 0444); |
117 | MODULE_PARM_DESC(mux_buf_sz_pkts, |
118 | "Size for the internal mux buffer in multiples of 188 bytes" ); |
119 | |
120 | static u32 vidtv_bridge_mux_buf_sz_for_mux_rate(void) |
121 | { |
122 | u32 max_elapsed_time_msecs = VIDTV_MAX_SLEEP_USECS / USEC_PER_MSEC; |
123 | u32 mux_buf_sz = mux_buf_sz_pkts * TS_PACKET_LEN; |
124 | u32 nbytes_expected; |
125 | |
126 | nbytes_expected = mux_rate_kbytes_sec; |
127 | nbytes_expected *= max_elapsed_time_msecs; |
128 | |
129 | mux_buf_sz = roundup(nbytes_expected, TS_PACKET_LEN); |
130 | mux_buf_sz += mux_buf_sz / 10; |
131 | |
132 | if (mux_buf_sz < MUX_BUF_MIN_SZ) |
133 | mux_buf_sz = MUX_BUF_MIN_SZ; |
134 | |
135 | if (mux_buf_sz > MUX_BUF_MAX_SZ) |
136 | mux_buf_sz = MUX_BUF_MAX_SZ; |
137 | |
138 | return mux_buf_sz; |
139 | } |
140 | |
141 | static bool vidtv_bridge_check_demod_lock(struct vidtv_dvb *dvb, u32 n) |
142 | { |
143 | enum fe_status status; |
144 | |
145 | dvb->fe[n]->ops.read_status(dvb->fe[n], &status); |
146 | |
147 | return status == (FE_HAS_SIGNAL | |
148 | FE_HAS_CARRIER | |
149 | FE_HAS_VITERBI | |
150 | FE_HAS_SYNC | |
151 | FE_HAS_LOCK); |
152 | } |
153 | |
154 | /* |
155 | * called on a separate thread by the mux when new packets become available |
156 | */ |
157 | static void vidtv_bridge_on_new_pkts_avail(void *priv, u8 *buf, u32 npkts) |
158 | { |
159 | struct vidtv_dvb *dvb = priv; |
160 | |
161 | /* drop packets if we lose the lock */ |
162 | if (vidtv_bridge_check_demod_lock(dvb, n: 0)) |
163 | dvb_dmx_swfilter_packets(demux: &dvb->demux, buf, count: npkts); |
164 | } |
165 | |
166 | static int vidtv_start_streaming(struct vidtv_dvb *dvb) |
167 | { |
168 | struct vidtv_mux_init_args mux_args = { |
169 | .mux_rate_kbytes_sec = mux_rate_kbytes_sec, |
170 | .on_new_packets_available_cb = vidtv_bridge_on_new_pkts_avail, |
171 | .pcr_period_usecs = pcr_period_msec * USEC_PER_MSEC, |
172 | .si_period_usecs = si_period_msec * USEC_PER_MSEC, |
173 | .pcr_pid = pcr_pid, |
174 | .transport_stream_id = VIDTV_DEFAULT_TS_ID, |
175 | .network_id = VIDTV_DEFAULT_NETWORK_ID, |
176 | .network_name = VIDTV_DEFAULT_NETWORK_NAME, |
177 | .priv = dvb, |
178 | }; |
179 | struct device *dev = &dvb->pdev->dev; |
180 | u32 mux_buf_sz; |
181 | |
182 | if (dvb->streaming) { |
183 | dev_warn_ratelimited(dev, "Already streaming. Skipping.\n" ); |
184 | return 0; |
185 | } |
186 | |
187 | if (mux_buf_sz_pkts) |
188 | mux_buf_sz = mux_buf_sz_pkts; |
189 | else |
190 | mux_buf_sz = vidtv_bridge_mux_buf_sz_for_mux_rate(); |
191 | |
192 | mux_args.mux_buf_sz = mux_buf_sz; |
193 | |
194 | dvb->streaming = true; |
195 | dvb->mux = vidtv_mux_init(fe: dvb->fe[0], dev, args: &mux_args); |
196 | if (!dvb->mux) |
197 | return -ENOMEM; |
198 | vidtv_mux_start_thread(m: dvb->mux); |
199 | |
200 | dev_dbg_ratelimited(dev, "Started streaming\n" ); |
201 | return 0; |
202 | } |
203 | |
204 | static int vidtv_stop_streaming(struct vidtv_dvb *dvb) |
205 | { |
206 | struct device *dev = &dvb->pdev->dev; |
207 | |
208 | dvb->streaming = false; |
209 | vidtv_mux_stop_thread(m: dvb->mux); |
210 | vidtv_mux_destroy(m: dvb->mux); |
211 | dvb->mux = NULL; |
212 | |
213 | dev_dbg_ratelimited(dev, "Stopped streaming\n" ); |
214 | return 0; |
215 | } |
216 | |
217 | static int vidtv_start_feed(struct dvb_demux_feed *feed) |
218 | { |
219 | struct dvb_demux *demux = feed->demux; |
220 | struct vidtv_dvb *dvb = demux->priv; |
221 | int ret; |
222 | int rc; |
223 | |
224 | if (!demux->dmx.frontend) |
225 | return -EINVAL; |
226 | |
227 | mutex_lock(&dvb->feed_lock); |
228 | |
229 | dvb->nfeeds++; |
230 | rc = dvb->nfeeds; |
231 | |
232 | if (dvb->nfeeds == 1) { |
233 | ret = vidtv_start_streaming(dvb); |
234 | if (ret < 0) |
235 | rc = ret; |
236 | } |
237 | |
238 | mutex_unlock(lock: &dvb->feed_lock); |
239 | return rc; |
240 | } |
241 | |
242 | static int vidtv_stop_feed(struct dvb_demux_feed *feed) |
243 | { |
244 | struct dvb_demux *demux = feed->demux; |
245 | struct vidtv_dvb *dvb = demux->priv; |
246 | int err = 0; |
247 | |
248 | mutex_lock(&dvb->feed_lock); |
249 | dvb->nfeeds--; |
250 | |
251 | if (!dvb->nfeeds) |
252 | err = vidtv_stop_streaming(dvb); |
253 | |
254 | mutex_unlock(lock: &dvb->feed_lock); |
255 | return err; |
256 | } |
257 | |
258 | static struct dvb_frontend *vidtv_get_frontend_ptr(struct i2c_client *c) |
259 | { |
260 | struct vidtv_demod_state *state = i2c_get_clientdata(client: c); |
261 | |
262 | /* the demod will set this when its probe function runs */ |
263 | return &state->frontend; |
264 | } |
265 | |
266 | static int vidtv_master_xfer(struct i2c_adapter *i2c_adap, |
267 | struct i2c_msg msgs[], |
268 | int num) |
269 | { |
270 | /* |
271 | * Right now, this virtual driver doesn't really send or receive |
272 | * messages from I2C. A real driver will require an implementation |
273 | * here. |
274 | */ |
275 | return 0; |
276 | } |
277 | |
278 | static u32 vidtv_i2c_func(struct i2c_adapter *adapter) |
279 | { |
280 | return I2C_FUNC_I2C; |
281 | } |
282 | |
283 | static const struct i2c_algorithm vidtv_i2c_algorithm = { |
284 | .master_xfer = vidtv_master_xfer, |
285 | .functionality = vidtv_i2c_func, |
286 | }; |
287 | |
288 | static int vidtv_bridge_i2c_register_adap(struct vidtv_dvb *dvb) |
289 | { |
290 | struct i2c_adapter *i2c_adapter = &dvb->i2c_adapter; |
291 | |
292 | strscpy(i2c_adapter->name, "vidtv_i2c" , sizeof(i2c_adapter->name)); |
293 | i2c_adapter->owner = THIS_MODULE; |
294 | i2c_adapter->algo = &vidtv_i2c_algorithm; |
295 | i2c_adapter->algo_data = NULL; |
296 | i2c_adapter->timeout = 500; |
297 | i2c_adapter->retries = 3; |
298 | i2c_adapter->dev.parent = &dvb->pdev->dev; |
299 | |
300 | i2c_set_adapdata(adap: i2c_adapter, data: dvb); |
301 | return i2c_add_adapter(adap: &dvb->i2c_adapter); |
302 | } |
303 | |
304 | static int vidtv_bridge_register_adap(struct vidtv_dvb *dvb) |
305 | { |
306 | int ret = 0; |
307 | |
308 | ret = dvb_register_adapter(adap: &dvb->adapter, |
309 | KBUILD_MODNAME, |
310 | THIS_MODULE, |
311 | device: &dvb->i2c_adapter.dev, |
312 | adapter_nums); |
313 | |
314 | return ret; |
315 | } |
316 | |
317 | static int vidtv_bridge_dmx_init(struct vidtv_dvb *dvb) |
318 | { |
319 | dvb->demux.dmx.capabilities = DMX_TS_FILTERING | |
320 | DMX_SECTION_FILTERING; |
321 | |
322 | dvb->demux.priv = dvb; |
323 | dvb->demux.filternum = 256; |
324 | dvb->demux.feednum = 256; |
325 | dvb->demux.start_feed = vidtv_start_feed; |
326 | dvb->demux.stop_feed = vidtv_stop_feed; |
327 | |
328 | return dvb_dmx_init(demux: &dvb->demux); |
329 | } |
330 | |
331 | static int vidtv_bridge_dmxdev_init(struct vidtv_dvb *dvb) |
332 | { |
333 | dvb->dmx_dev.filternum = 256; |
334 | dvb->dmx_dev.demux = &dvb->demux.dmx; |
335 | dvb->dmx_dev.capabilities = 0; |
336 | |
337 | return dvb_dmxdev_init(dmxdev: &dvb->dmx_dev, adap: &dvb->adapter); |
338 | } |
339 | |
340 | static int vidtv_bridge_probe_demod(struct vidtv_dvb *dvb, u32 n) |
341 | { |
342 | struct vidtv_demod_config cfg = { |
343 | .drop_tslock_prob_on_low_snr = drop_tslock_prob_on_low_snr, |
344 | .recover_tslock_prob_on_good_snr = recover_tslock_prob_on_good_snr, |
345 | }; |
346 | dvb->i2c_client_demod[n] = dvb_module_probe(module_name: "dvb_vidtv_demod" , |
347 | NULL, |
348 | adap: &dvb->i2c_adapter, |
349 | DEMOD_DEFAULT_ADDR, |
350 | platform_data: &cfg); |
351 | |
352 | /* driver will not work anyways so bail out */ |
353 | if (!dvb->i2c_client_demod[n]) |
354 | return -ENODEV; |
355 | |
356 | /* retrieve a ptr to the frontend state */ |
357 | dvb->fe[n] = vidtv_get_frontend_ptr(c: dvb->i2c_client_demod[n]); |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n) |
363 | { |
364 | struct vidtv_tuner_config cfg = { |
365 | .fe = dvb->fe[n], |
366 | .mock_power_up_delay_msec = mock_power_up_delay_msec, |
367 | .mock_tune_delay_msec = mock_tune_delay_msec, |
368 | }; |
369 | u32 freq; |
370 | int i; |
371 | |
372 | /* TODO: check if the frequencies are at a valid range */ |
373 | |
374 | memcpy(cfg.vidtv_valid_dvb_t_freqs, |
375 | vidtv_valid_dvb_t_freqs, |
376 | sizeof(vidtv_valid_dvb_t_freqs)); |
377 | |
378 | memcpy(cfg.vidtv_valid_dvb_c_freqs, |
379 | vidtv_valid_dvb_c_freqs, |
380 | sizeof(vidtv_valid_dvb_c_freqs)); |
381 | |
382 | /* |
383 | * Convert Satellite frequencies from Ku-band in kHZ into S-band |
384 | * frequencies in Hz. |
385 | */ |
386 | for (i = 0; i < ARRAY_SIZE(vidtv_valid_dvb_s_freqs); i++) { |
387 | freq = vidtv_valid_dvb_s_freqs[i]; |
388 | if (freq) { |
389 | if (freq < LNB_CUT_FREQUENCY) |
390 | freq = abs(freq - LNB_LOW_FREQ); |
391 | else |
392 | freq = abs(freq - LNB_HIGH_FREQ); |
393 | } |
394 | cfg.vidtv_valid_dvb_s_freqs[i] = freq; |
395 | } |
396 | |
397 | cfg.max_frequency_shift_hz = max_frequency_shift_hz; |
398 | |
399 | dvb->i2c_client_tuner[n] = dvb_module_probe(module_name: "dvb_vidtv_tuner" , |
400 | NULL, |
401 | adap: &dvb->i2c_adapter, |
402 | TUNER_DEFAULT_ADDR, |
403 | platform_data: &cfg); |
404 | |
405 | return (dvb->i2c_client_tuner[n]) ? 0 : -ENODEV; |
406 | } |
407 | |
408 | static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb) |
409 | { |
410 | int ret, i, j; |
411 | |
412 | ret = vidtv_bridge_i2c_register_adap(dvb); |
413 | if (ret < 0) |
414 | goto fail_i2c; |
415 | |
416 | ret = vidtv_bridge_register_adap(dvb); |
417 | if (ret < 0) |
418 | goto fail_adapter; |
419 | dvb_register_media_controller(adap: &dvb->adapter, mdev: &dvb->mdev); |
420 | |
421 | for (i = 0; i < NUM_FE; ++i) { |
422 | ret = vidtv_bridge_probe_demod(dvb, n: i); |
423 | if (ret < 0) |
424 | goto fail_demod_probe; |
425 | |
426 | ret = vidtv_bridge_probe_tuner(dvb, n: i); |
427 | if (ret < 0) |
428 | goto fail_tuner_probe; |
429 | |
430 | ret = dvb_register_frontend(dvb: &dvb->adapter, fe: dvb->fe[i]); |
431 | if (ret < 0) |
432 | goto fail_fe; |
433 | } |
434 | |
435 | ret = vidtv_bridge_dmx_init(dvb); |
436 | if (ret < 0) |
437 | goto fail_dmx; |
438 | |
439 | ret = vidtv_bridge_dmxdev_init(dvb); |
440 | if (ret < 0) |
441 | goto fail_dmx_dev; |
442 | |
443 | for (j = 0; j < NUM_FE; ++j) { |
444 | ret = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, |
445 | &dvb->dmx_fe[j]); |
446 | if (ret < 0) |
447 | goto fail_dmx_conn; |
448 | |
449 | /* |
450 | * The source of the demux is a frontend connected |
451 | * to the demux. |
452 | */ |
453 | dvb->dmx_fe[j].source = DMX_FRONTEND_0; |
454 | } |
455 | |
456 | return ret; |
457 | |
458 | fail_dmx_conn: |
459 | for (j = j - 1; j >= 0; --j) |
460 | dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, |
461 | &dvb->dmx_fe[j]); |
462 | dvb_dmxdev_release(dmxdev: &dvb->dmx_dev); |
463 | fail_dmx_dev: |
464 | dvb_dmx_release(demux: &dvb->demux); |
465 | fail_dmx: |
466 | fail_demod_probe: |
467 | for (i = i - 1; i >= 0; --i) { |
468 | dvb_unregister_frontend(fe: dvb->fe[i]); |
469 | fail_fe: |
470 | dvb_module_release(client: dvb->i2c_client_tuner[i]); |
471 | fail_tuner_probe: |
472 | dvb_module_release(client: dvb->i2c_client_demod[i]); |
473 | } |
474 | fail_adapter: |
475 | dvb_unregister_adapter(adap: &dvb->adapter); |
476 | fail_i2c: |
477 | i2c_del_adapter(adap: &dvb->i2c_adapter); |
478 | |
479 | return ret; |
480 | } |
481 | |
482 | static int vidtv_bridge_probe(struct platform_device *pdev) |
483 | { |
484 | struct vidtv_dvb *dvb; |
485 | int ret; |
486 | |
487 | dvb = kzalloc(size: sizeof(*dvb), GFP_KERNEL); |
488 | if (!dvb) |
489 | return -ENOMEM; |
490 | |
491 | dvb->pdev = pdev; |
492 | |
493 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
494 | dvb->mdev.dev = &pdev->dev; |
495 | |
496 | strscpy(dvb->mdev.model, "vidtv" , sizeof(dvb->mdev.model)); |
497 | strscpy(dvb->mdev.bus_info, "platform:vidtv" , sizeof(dvb->mdev.bus_info)); |
498 | |
499 | media_device_init(mdev: &dvb->mdev); |
500 | #endif |
501 | |
502 | ret = vidtv_bridge_dvb_init(dvb); |
503 | if (ret < 0) |
504 | goto err_dvb; |
505 | |
506 | mutex_init(&dvb->feed_lock); |
507 | |
508 | platform_set_drvdata(pdev, data: dvb); |
509 | |
510 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
511 | ret = media_device_register(&dvb->mdev); |
512 | if (ret) { |
513 | dev_err(dvb->mdev.dev, |
514 | "media device register failed (err=%d)\n" , ret); |
515 | goto err_media_device_register; |
516 | } |
517 | #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ |
518 | |
519 | dev_info(&pdev->dev, "Successfully initialized vidtv!\n" ); |
520 | return ret; |
521 | |
522 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
523 | err_media_device_register: |
524 | media_device_cleanup(mdev: &dvb->mdev); |
525 | #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ |
526 | err_dvb: |
527 | kfree(objp: dvb); |
528 | return ret; |
529 | } |
530 | |
531 | static void vidtv_bridge_remove(struct platform_device *pdev) |
532 | { |
533 | struct vidtv_dvb *dvb; |
534 | u32 i; |
535 | |
536 | dvb = platform_get_drvdata(pdev); |
537 | |
538 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
539 | media_device_unregister(mdev: &dvb->mdev); |
540 | media_device_cleanup(mdev: &dvb->mdev); |
541 | #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ |
542 | |
543 | mutex_destroy(lock: &dvb->feed_lock); |
544 | |
545 | for (i = 0; i < NUM_FE; ++i) { |
546 | dvb_unregister_frontend(fe: dvb->fe[i]); |
547 | dvb_module_release(client: dvb->i2c_client_tuner[i]); |
548 | dvb_module_release(client: dvb->i2c_client_demod[i]); |
549 | } |
550 | |
551 | dvb_dmxdev_release(dmxdev: &dvb->dmx_dev); |
552 | dvb_dmx_release(demux: &dvb->demux); |
553 | dvb_unregister_adapter(adap: &dvb->adapter); |
554 | dev_info(&pdev->dev, "Successfully removed vidtv\n" ); |
555 | } |
556 | |
557 | static void vidtv_bridge_dev_release(struct device *dev) |
558 | { |
559 | struct vidtv_dvb *dvb; |
560 | |
561 | dvb = dev_get_drvdata(dev); |
562 | kfree(objp: dvb); |
563 | } |
564 | |
565 | static struct platform_device vidtv_bridge_dev = { |
566 | .name = VIDTV_PDEV_NAME, |
567 | .dev.release = vidtv_bridge_dev_release, |
568 | }; |
569 | |
570 | static struct platform_driver vidtv_bridge_driver = { |
571 | .driver = { |
572 | .name = VIDTV_PDEV_NAME, |
573 | }, |
574 | .probe = vidtv_bridge_probe, |
575 | .remove_new = vidtv_bridge_remove, |
576 | }; |
577 | |
578 | static void __exit vidtv_bridge_exit(void) |
579 | { |
580 | platform_driver_unregister(&vidtv_bridge_driver); |
581 | platform_device_unregister(&vidtv_bridge_dev); |
582 | } |
583 | |
584 | static int __init vidtv_bridge_init(void) |
585 | { |
586 | int ret; |
587 | |
588 | ret = platform_device_register(&vidtv_bridge_dev); |
589 | if (ret) |
590 | return ret; |
591 | |
592 | ret = platform_driver_register(&vidtv_bridge_driver); |
593 | if (ret) |
594 | platform_device_unregister(&vidtv_bridge_dev); |
595 | |
596 | return ret; |
597 | } |
598 | |
599 | module_init(vidtv_bridge_init); |
600 | module_exit(vidtv_bridge_exit); |
601 | |
602 | MODULE_DESCRIPTION("Virtual Digital TV Test Driver" ); |
603 | MODULE_AUTHOR("Daniel W. S. Almeida" ); |
604 | MODULE_LICENSE("GPL" ); |
605 | MODULE_ALIAS("vidtv" ); |
606 | MODULE_ALIAS("dvb_vidtv" ); |
607 | |