1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Abilis Systems Single DVB-T Receiver
4*4882a593Smuzhiyun * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
5*4882a593Smuzhiyun * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/errno.h>
9*4882a593Smuzhiyun #include <linux/slab.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/mm.h>
12*4882a593Smuzhiyun #include <linux/kref.h>
13*4882a593Smuzhiyun #include <linux/uaccess.h>
14*4882a593Smuzhiyun #include <linux/usb.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun /* header file for usb device driver*/
17*4882a593Smuzhiyun #include "as102_drv.h"
18*4882a593Smuzhiyun #include "as10x_cmd.h"
19*4882a593Smuzhiyun #include "as102_fe.h"
20*4882a593Smuzhiyun #include "as102_fw.h"
21*4882a593Smuzhiyun #include <media/dvbdev.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun int dual_tuner;
24*4882a593Smuzhiyun module_param_named(dual_tuner, dual_tuner, int, 0644);
25*4882a593Smuzhiyun MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)");
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static int fw_upload = 1;
28*4882a593Smuzhiyun module_param_named(fw_upload, fw_upload, int, 0644);
29*4882a593Smuzhiyun MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)");
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun static int pid_filtering;
32*4882a593Smuzhiyun module_param_named(pid_filtering, pid_filtering, int, 0644);
33*4882a593Smuzhiyun MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)");
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static int ts_auto_disable;
36*4882a593Smuzhiyun module_param_named(ts_auto_disable, ts_auto_disable, int, 0644);
37*4882a593Smuzhiyun MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)");
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun int elna_enable = 1;
40*4882a593Smuzhiyun module_param_named(elna_enable, elna_enable, int, 0644);
41*4882a593Smuzhiyun MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)");
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
44*4882a593Smuzhiyun
as102_stop_stream(struct as102_dev_t * dev)45*4882a593Smuzhiyun static void as102_stop_stream(struct as102_dev_t *dev)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (dev != NULL)
50*4882a593Smuzhiyun bus_adap = &dev->bus_adap;
51*4882a593Smuzhiyun else
52*4882a593Smuzhiyun return;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun if (bus_adap->ops->stop_stream != NULL)
55*4882a593Smuzhiyun bus_adap->ops->stop_stream(dev);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (ts_auto_disable) {
58*4882a593Smuzhiyun if (mutex_lock_interruptible(&dev->bus_adap.lock))
59*4882a593Smuzhiyun return;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun if (as10x_cmd_stop_streaming(bus_adap) < 0)
62*4882a593Smuzhiyun dev_dbg(&dev->bus_adap.usb_dev->dev,
63*4882a593Smuzhiyun "as10x_cmd_stop_streaming failed\n");
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun mutex_unlock(&dev->bus_adap.lock);
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
as102_start_stream(struct as102_dev_t * dev)69*4882a593Smuzhiyun static int as102_start_stream(struct as102_dev_t *dev)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap;
72*4882a593Smuzhiyun int ret = -EFAULT;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun if (dev != NULL)
75*4882a593Smuzhiyun bus_adap = &dev->bus_adap;
76*4882a593Smuzhiyun else
77*4882a593Smuzhiyun return ret;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (bus_adap->ops->start_stream != NULL)
80*4882a593Smuzhiyun ret = bus_adap->ops->start_stream(dev);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun if (ts_auto_disable) {
83*4882a593Smuzhiyun if (mutex_lock_interruptible(&dev->bus_adap.lock))
84*4882a593Smuzhiyun return -EFAULT;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun ret = as10x_cmd_start_streaming(bus_adap);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun mutex_unlock(&dev->bus_adap.lock);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun return ret;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
as10x_pid_filter(struct as102_dev_t * dev,int index,u16 pid,int onoff)94*4882a593Smuzhiyun static int as10x_pid_filter(struct as102_dev_t *dev,
95*4882a593Smuzhiyun int index, u16 pid, int onoff) {
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap;
98*4882a593Smuzhiyun int ret = -EFAULT;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (mutex_lock_interruptible(&dev->bus_adap.lock)) {
101*4882a593Smuzhiyun dev_dbg(&dev->bus_adap.usb_dev->dev,
102*4882a593Smuzhiyun "amutex_lock_interruptible(lock) failed !\n");
103*4882a593Smuzhiyun return -EBUSY;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun switch (onoff) {
107*4882a593Smuzhiyun case 0:
108*4882a593Smuzhiyun ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid);
109*4882a593Smuzhiyun dev_dbg(&dev->bus_adap.usb_dev->dev,
110*4882a593Smuzhiyun "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n",
111*4882a593Smuzhiyun index, pid, ret);
112*4882a593Smuzhiyun break;
113*4882a593Smuzhiyun case 1:
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun struct as10x_ts_filter filter;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun filter.type = TS_PID_TYPE_TS;
118*4882a593Smuzhiyun filter.idx = 0xFF;
119*4882a593Smuzhiyun filter.pid = pid;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun ret = as10x_cmd_add_PID_filter(bus_adap, &filter);
122*4882a593Smuzhiyun dev_dbg(&dev->bus_adap.usb_dev->dev,
123*4882a593Smuzhiyun "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n",
124*4882a593Smuzhiyun index, filter.idx, filter.pid, ret);
125*4882a593Smuzhiyun break;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun mutex_unlock(&dev->bus_adap.lock);
130*4882a593Smuzhiyun return ret;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
as102_dvb_dmx_start_feed(struct dvb_demux_feed * dvbdmxfeed)133*4882a593Smuzhiyun static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun int ret = 0;
136*4882a593Smuzhiyun struct dvb_demux *demux = dvbdmxfeed->demux;
137*4882a593Smuzhiyun struct as102_dev_t *as102_dev = demux->priv;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun if (mutex_lock_interruptible(&as102_dev->sem))
140*4882a593Smuzhiyun return -ERESTARTSYS;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (pid_filtering)
143*4882a593Smuzhiyun as10x_pid_filter(as102_dev, dvbdmxfeed->index,
144*4882a593Smuzhiyun dvbdmxfeed->pid, 1);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun if (as102_dev->streaming++ == 0)
147*4882a593Smuzhiyun ret = as102_start_stream(as102_dev);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun mutex_unlock(&as102_dev->sem);
150*4882a593Smuzhiyun return ret;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
as102_dvb_dmx_stop_feed(struct dvb_demux_feed * dvbdmxfeed)153*4882a593Smuzhiyun static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun struct dvb_demux *demux = dvbdmxfeed->demux;
156*4882a593Smuzhiyun struct as102_dev_t *as102_dev = demux->priv;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (mutex_lock_interruptible(&as102_dev->sem))
159*4882a593Smuzhiyun return -ERESTARTSYS;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun if (--as102_dev->streaming == 0)
162*4882a593Smuzhiyun as102_stop_stream(as102_dev);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (pid_filtering)
165*4882a593Smuzhiyun as10x_pid_filter(as102_dev, dvbdmxfeed->index,
166*4882a593Smuzhiyun dvbdmxfeed->pid, 0);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun mutex_unlock(&as102_dev->sem);
169*4882a593Smuzhiyun return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
as102_set_tune(void * priv,struct as10x_tune_args * tune_args)172*4882a593Smuzhiyun static int as102_set_tune(void *priv, struct as10x_tune_args *tune_args)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = priv;
175*4882a593Smuzhiyun int ret;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun /* Set frontend arguments */
178*4882a593Smuzhiyun if (mutex_lock_interruptible(&bus_adap->lock))
179*4882a593Smuzhiyun return -EBUSY;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun ret = as10x_cmd_set_tune(bus_adap, tune_args);
182*4882a593Smuzhiyun if (ret != 0)
183*4882a593Smuzhiyun dev_dbg(&bus_adap->usb_dev->dev,
184*4882a593Smuzhiyun "as10x_cmd_set_tune failed. (err = %d)\n", ret);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun mutex_unlock(&bus_adap->lock);
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun return ret;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
as102_get_tps(void * priv,struct as10x_tps * tps)191*4882a593Smuzhiyun static int as102_get_tps(void *priv, struct as10x_tps *tps)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = priv;
194*4882a593Smuzhiyun int ret;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun if (mutex_lock_interruptible(&bus_adap->lock))
197*4882a593Smuzhiyun return -EBUSY;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun /* send abilis command: GET_TPS */
200*4882a593Smuzhiyun ret = as10x_cmd_get_tps(bus_adap, tps);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun mutex_unlock(&bus_adap->lock);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun return ret;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
as102_get_status(void * priv,struct as10x_tune_status * tstate)207*4882a593Smuzhiyun static int as102_get_status(void *priv, struct as10x_tune_status *tstate)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = priv;
210*4882a593Smuzhiyun int ret;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun if (mutex_lock_interruptible(&bus_adap->lock))
213*4882a593Smuzhiyun return -EBUSY;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* send abilis command: GET_TUNE_STATUS */
216*4882a593Smuzhiyun ret = as10x_cmd_get_tune_status(bus_adap, tstate);
217*4882a593Smuzhiyun if (ret < 0) {
218*4882a593Smuzhiyun dev_dbg(&bus_adap->usb_dev->dev,
219*4882a593Smuzhiyun "as10x_cmd_get_tune_status failed (err = %d)\n",
220*4882a593Smuzhiyun ret);
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun mutex_unlock(&bus_adap->lock);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun return ret;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
as102_get_stats(void * priv,struct as10x_demod_stats * demod_stats)228*4882a593Smuzhiyun static int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = priv;
231*4882a593Smuzhiyun int ret;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun if (mutex_lock_interruptible(&bus_adap->lock))
234*4882a593Smuzhiyun return -EBUSY;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun /* send abilis command: GET_TUNE_STATUS */
237*4882a593Smuzhiyun ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats);
238*4882a593Smuzhiyun if (ret < 0) {
239*4882a593Smuzhiyun dev_dbg(&bus_adap->usb_dev->dev,
240*4882a593Smuzhiyun "as10x_cmd_get_demod_stats failed (probably not tuned)\n");
241*4882a593Smuzhiyun } else {
242*4882a593Smuzhiyun dev_dbg(&bus_adap->usb_dev->dev,
243*4882a593Smuzhiyun "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n",
244*4882a593Smuzhiyun demod_stats->frame_count,
245*4882a593Smuzhiyun demod_stats->bad_frame_count,
246*4882a593Smuzhiyun demod_stats->bytes_fixed_by_rs,
247*4882a593Smuzhiyun demod_stats->mer);
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun mutex_unlock(&bus_adap->lock);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun return ret;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
as102_stream_ctrl(void * priv,int acquire,uint32_t elna_cfg)254*4882a593Smuzhiyun static int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun struct as10x_bus_adapter_t *bus_adap = priv;
257*4882a593Smuzhiyun int ret;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (mutex_lock_interruptible(&bus_adap->lock))
260*4882a593Smuzhiyun return -EBUSY;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun if (acquire) {
263*4882a593Smuzhiyun if (elna_enable)
264*4882a593Smuzhiyun as10x_cmd_set_context(bus_adap,
265*4882a593Smuzhiyun CONTEXT_LNA, elna_cfg);
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun ret = as10x_cmd_turn_on(bus_adap);
268*4882a593Smuzhiyun } else {
269*4882a593Smuzhiyun ret = as10x_cmd_turn_off(bus_adap);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun mutex_unlock(&bus_adap->lock);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun return ret;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun static const struct as102_fe_ops as102_fe_ops = {
278*4882a593Smuzhiyun .set_tune = as102_set_tune,
279*4882a593Smuzhiyun .get_tps = as102_get_tps,
280*4882a593Smuzhiyun .get_status = as102_get_status,
281*4882a593Smuzhiyun .get_stats = as102_get_stats,
282*4882a593Smuzhiyun .stream_ctrl = as102_stream_ctrl,
283*4882a593Smuzhiyun };
284*4882a593Smuzhiyun
as102_dvb_register(struct as102_dev_t * as102_dev)285*4882a593Smuzhiyun int as102_dvb_register(struct as102_dev_t *as102_dev)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun struct device *dev = &as102_dev->bus_adap.usb_dev->dev;
288*4882a593Smuzhiyun int ret;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun ret = dvb_register_adapter(&as102_dev->dvb_adap,
291*4882a593Smuzhiyun as102_dev->name, THIS_MODULE,
292*4882a593Smuzhiyun dev, adapter_nr);
293*4882a593Smuzhiyun if (ret < 0) {
294*4882a593Smuzhiyun dev_err(dev, "%s: dvb_register_adapter() failed: %d\n",
295*4882a593Smuzhiyun __func__, ret);
296*4882a593Smuzhiyun return ret;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun as102_dev->dvb_dmx.priv = as102_dev;
300*4882a593Smuzhiyun as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256;
301*4882a593Smuzhiyun as102_dev->dvb_dmx.feednum = 256;
302*4882a593Smuzhiyun as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed;
303*4882a593Smuzhiyun as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING |
306*4882a593Smuzhiyun DMX_SECTION_FILTERING;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum;
309*4882a593Smuzhiyun as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx;
310*4882a593Smuzhiyun as102_dev->dvb_dmxdev.capabilities = 0;
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun ret = dvb_dmx_init(&as102_dev->dvb_dmx);
313*4882a593Smuzhiyun if (ret < 0) {
314*4882a593Smuzhiyun dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret);
315*4882a593Smuzhiyun goto edmxinit;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap);
319*4882a593Smuzhiyun if (ret < 0) {
320*4882a593Smuzhiyun dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n",
321*4882a593Smuzhiyun __func__, ret);
322*4882a593Smuzhiyun goto edmxdinit;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /* Attach the frontend */
326*4882a593Smuzhiyun as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name,
327*4882a593Smuzhiyun &as102_fe_ops,
328*4882a593Smuzhiyun &as102_dev->bus_adap,
329*4882a593Smuzhiyun as102_dev->elna_cfg);
330*4882a593Smuzhiyun if (!as102_dev->dvb_fe) {
331*4882a593Smuzhiyun ret = -ENODEV;
332*4882a593Smuzhiyun dev_err(dev, "%s: as102_attach() failed: %d",
333*4882a593Smuzhiyun __func__, ret);
334*4882a593Smuzhiyun goto efereg;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun ret = dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe);
338*4882a593Smuzhiyun if (ret < 0) {
339*4882a593Smuzhiyun dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d",
340*4882a593Smuzhiyun __func__, ret);
341*4882a593Smuzhiyun goto efereg;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun /* init bus mutex for token locking */
345*4882a593Smuzhiyun mutex_init(&as102_dev->bus_adap.lock);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun /* init start / stop stream mutex */
348*4882a593Smuzhiyun mutex_init(&as102_dev->sem);
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun /*
351*4882a593Smuzhiyun * try to load as102 firmware. If firmware upload failed, we'll be
352*4882a593Smuzhiyun * able to upload it later.
353*4882a593Smuzhiyun */
354*4882a593Smuzhiyun if (fw_upload)
355*4882a593Smuzhiyun try_then_request_module(as102_fw_upload(&as102_dev->bus_adap),
356*4882a593Smuzhiyun "firmware_class");
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun pr_info("Registered device %s", as102_dev->name);
359*4882a593Smuzhiyun return 0;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun efereg:
362*4882a593Smuzhiyun dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
363*4882a593Smuzhiyun edmxdinit:
364*4882a593Smuzhiyun dvb_dmx_release(&as102_dev->dvb_dmx);
365*4882a593Smuzhiyun edmxinit:
366*4882a593Smuzhiyun dvb_unregister_adapter(&as102_dev->dvb_adap);
367*4882a593Smuzhiyun return ret;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
as102_dvb_unregister(struct as102_dev_t * as102_dev)370*4882a593Smuzhiyun void as102_dvb_unregister(struct as102_dev_t *as102_dev)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun /* unregister as102 frontend */
373*4882a593Smuzhiyun dvb_unregister_frontend(as102_dev->dvb_fe);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun /* detach frontend */
376*4882a593Smuzhiyun dvb_frontend_detach(as102_dev->dvb_fe);
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /* unregister demux device */
379*4882a593Smuzhiyun dvb_dmxdev_release(&as102_dev->dvb_dmxdev);
380*4882a593Smuzhiyun dvb_dmx_release(&as102_dev->dvb_dmx);
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun /* unregister dvb adapter */
383*4882a593Smuzhiyun dvb_unregister_adapter(&as102_dev->dvb_adap);
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun pr_info("Unregistered device %s", as102_dev->name);
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun module_usb_driver(as102_usb_driver);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun /* modinfo details */
391*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_FULL_NAME);
392*4882a593Smuzhiyun MODULE_LICENSE("GPL");
393*4882a593Smuzhiyun MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");
394