162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Abilis Systems Single DVB-T Receiver 462306a36Sopenharmony_ci * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> 562306a36Sopenharmony_ci * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/kref.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* header file for usb device driver*/ 1762306a36Sopenharmony_ci#include "as102_drv.h" 1862306a36Sopenharmony_ci#include "as10x_cmd.h" 1962306a36Sopenharmony_ci#include "as102_fe.h" 2062306a36Sopenharmony_ci#include "as102_fw.h" 2162306a36Sopenharmony_ci#include <media/dvbdev.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint dual_tuner; 2462306a36Sopenharmony_cimodule_param_named(dual_tuner, dual_tuner, int, 0644); 2562306a36Sopenharmony_ciMODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int fw_upload = 1; 2862306a36Sopenharmony_cimodule_param_named(fw_upload, fw_upload, int, 0644); 2962306a36Sopenharmony_ciMODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int pid_filtering; 3262306a36Sopenharmony_cimodule_param_named(pid_filtering, pid_filtering, int, 0644); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int ts_auto_disable; 3662306a36Sopenharmony_cimodule_param_named(ts_auto_disable, ts_auto_disable, int, 0644); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)"); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint elna_enable = 1; 4062306a36Sopenharmony_cimodule_param_named(elna_enable, elna_enable, int, 0644); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void as102_stop_stream(struct as102_dev_t *dev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (dev != NULL) 5062306a36Sopenharmony_ci bus_adap = &dev->bus_adap; 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (bus_adap->ops->stop_stream != NULL) 5562306a36Sopenharmony_ci bus_adap->ops->stop_stream(dev); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (ts_auto_disable) { 5862306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->bus_adap.lock)) 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (as10x_cmd_stop_streaming(bus_adap) < 0) 6262306a36Sopenharmony_ci dev_dbg(&dev->bus_adap.usb_dev->dev, 6362306a36Sopenharmony_ci "as10x_cmd_stop_streaming failed\n"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci mutex_unlock(&dev->bus_adap.lock); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int as102_start_stream(struct as102_dev_t *dev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap; 7262306a36Sopenharmony_ci int ret = -EFAULT; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (dev != NULL) 7562306a36Sopenharmony_ci bus_adap = &dev->bus_adap; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci return ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (bus_adap->ops->start_stream != NULL) 8062306a36Sopenharmony_ci ret = bus_adap->ops->start_stream(dev); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (ts_auto_disable) { 8362306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->bus_adap.lock)) 8462306a36Sopenharmony_ci return -EFAULT; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = as10x_cmd_start_streaming(bus_adap); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci mutex_unlock(&dev->bus_adap.lock); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int as10x_pid_filter(struct as102_dev_t *dev, 9562306a36Sopenharmony_ci int index, u16 pid, int onoff) { 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap; 9862306a36Sopenharmony_ci int ret = -EFAULT; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->bus_adap.lock)) { 10162306a36Sopenharmony_ci dev_dbg(&dev->bus_adap.usb_dev->dev, 10262306a36Sopenharmony_ci "amutex_lock_interruptible(lock) failed !\n"); 10362306a36Sopenharmony_ci return -EBUSY; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci switch (onoff) { 10762306a36Sopenharmony_ci case 0: 10862306a36Sopenharmony_ci ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid); 10962306a36Sopenharmony_ci dev_dbg(&dev->bus_adap.usb_dev->dev, 11062306a36Sopenharmony_ci "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n", 11162306a36Sopenharmony_ci index, pid, ret); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case 1: 11462306a36Sopenharmony_ci { 11562306a36Sopenharmony_ci struct as10x_ts_filter filter; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci filter.type = TS_PID_TYPE_TS; 11862306a36Sopenharmony_ci filter.idx = 0xFF; 11962306a36Sopenharmony_ci filter.pid = pid; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = as10x_cmd_add_PID_filter(bus_adap, &filter); 12262306a36Sopenharmony_ci dev_dbg(&dev->bus_adap.usb_dev->dev, 12362306a36Sopenharmony_ci "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", 12462306a36Sopenharmony_ci index, filter.idx, filter.pid, ret); 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci mutex_unlock(&dev->bus_adap.lock); 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int ret = 0; 13662306a36Sopenharmony_ci struct dvb_demux *demux = dvbdmxfeed->demux; 13762306a36Sopenharmony_ci struct as102_dev_t *as102_dev = demux->priv; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (mutex_lock_interruptible(&as102_dev->sem)) 14062306a36Sopenharmony_ci return -ERESTARTSYS; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (pid_filtering) 14362306a36Sopenharmony_ci as10x_pid_filter(as102_dev, dvbdmxfeed->index, 14462306a36Sopenharmony_ci dvbdmxfeed->pid, 1); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (as102_dev->streaming++ == 0) 14762306a36Sopenharmony_ci ret = as102_start_stream(as102_dev); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci mutex_unlock(&as102_dev->sem); 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct dvb_demux *demux = dvbdmxfeed->demux; 15662306a36Sopenharmony_ci struct as102_dev_t *as102_dev = demux->priv; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (mutex_lock_interruptible(&as102_dev->sem)) 15962306a36Sopenharmony_ci return -ERESTARTSYS; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (--as102_dev->streaming == 0) 16262306a36Sopenharmony_ci as102_stop_stream(as102_dev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (pid_filtering) 16562306a36Sopenharmony_ci as10x_pid_filter(as102_dev, dvbdmxfeed->index, 16662306a36Sopenharmony_ci dvbdmxfeed->pid, 0); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci mutex_unlock(&as102_dev->sem); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int as102_set_tune(void *priv, struct as10x_tune_args *tune_args) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = priv; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Set frontend arguments */ 17862306a36Sopenharmony_ci if (mutex_lock_interruptible(&bus_adap->lock)) 17962306a36Sopenharmony_ci return -EBUSY; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = as10x_cmd_set_tune(bus_adap, tune_args); 18262306a36Sopenharmony_ci if (ret != 0) 18362306a36Sopenharmony_ci dev_dbg(&bus_adap->usb_dev->dev, 18462306a36Sopenharmony_ci "as10x_cmd_set_tune failed. (err = %d)\n", ret); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mutex_unlock(&bus_adap->lock); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int as102_get_tps(void *priv, struct as10x_tps *tps) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = priv; 19462306a36Sopenharmony_ci int ret; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (mutex_lock_interruptible(&bus_adap->lock)) 19762306a36Sopenharmony_ci return -EBUSY; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* send abilis command: GET_TPS */ 20062306a36Sopenharmony_ci ret = as10x_cmd_get_tps(bus_adap, tps); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci mutex_unlock(&bus_adap->lock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int as102_get_status(void *priv, struct as10x_tune_status *tstate) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = priv; 21062306a36Sopenharmony_ci int ret; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (mutex_lock_interruptible(&bus_adap->lock)) 21362306a36Sopenharmony_ci return -EBUSY; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* send abilis command: GET_TUNE_STATUS */ 21662306a36Sopenharmony_ci ret = as10x_cmd_get_tune_status(bus_adap, tstate); 21762306a36Sopenharmony_ci if (ret < 0) { 21862306a36Sopenharmony_ci dev_dbg(&bus_adap->usb_dev->dev, 21962306a36Sopenharmony_ci "as10x_cmd_get_tune_status failed (err = %d)\n", 22062306a36Sopenharmony_ci ret); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mutex_unlock(&bus_adap->lock); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int as102_get_stats(void *priv, struct as10x_demod_stats *demod_stats) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = priv; 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (mutex_lock_interruptible(&bus_adap->lock)) 23462306a36Sopenharmony_ci return -EBUSY; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* send abilis command: GET_TUNE_STATUS */ 23762306a36Sopenharmony_ci ret = as10x_cmd_get_demod_stats(bus_adap, demod_stats); 23862306a36Sopenharmony_ci if (ret < 0) { 23962306a36Sopenharmony_ci dev_dbg(&bus_adap->usb_dev->dev, 24062306a36Sopenharmony_ci "as10x_cmd_get_demod_stats failed (probably not tuned)\n"); 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci dev_dbg(&bus_adap->usb_dev->dev, 24362306a36Sopenharmony_ci "demod status: fc: 0x%08x, bad fc: 0x%08x, bytes corrected: 0x%08x , MER: 0x%04x\n", 24462306a36Sopenharmony_ci demod_stats->frame_count, 24562306a36Sopenharmony_ci demod_stats->bad_frame_count, 24662306a36Sopenharmony_ci demod_stats->bytes_fixed_by_rs, 24762306a36Sopenharmony_ci demod_stats->mer); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci mutex_unlock(&bus_adap->lock); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int as102_stream_ctrl(void *priv, int acquire, uint32_t elna_cfg) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct as10x_bus_adapter_t *bus_adap = priv; 25762306a36Sopenharmony_ci int ret; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (mutex_lock_interruptible(&bus_adap->lock)) 26062306a36Sopenharmony_ci return -EBUSY; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (acquire) { 26362306a36Sopenharmony_ci if (elna_enable) 26462306a36Sopenharmony_ci as10x_cmd_set_context(bus_adap, 26562306a36Sopenharmony_ci CONTEXT_LNA, elna_cfg); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = as10x_cmd_turn_on(bus_adap); 26862306a36Sopenharmony_ci } else { 26962306a36Sopenharmony_ci ret = as10x_cmd_turn_off(bus_adap); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci mutex_unlock(&bus_adap->lock); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct as102_fe_ops as102_fe_ops = { 27862306a36Sopenharmony_ci .set_tune = as102_set_tune, 27962306a36Sopenharmony_ci .get_tps = as102_get_tps, 28062306a36Sopenharmony_ci .get_status = as102_get_status, 28162306a36Sopenharmony_ci .get_stats = as102_get_stats, 28262306a36Sopenharmony_ci .stream_ctrl = as102_stream_ctrl, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciint as102_dvb_register(struct as102_dev_t *as102_dev) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct device *dev = &as102_dev->bus_adap.usb_dev->dev; 28862306a36Sopenharmony_ci int ret; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret = dvb_register_adapter(&as102_dev->dvb_adap, 29162306a36Sopenharmony_ci as102_dev->name, THIS_MODULE, 29262306a36Sopenharmony_ci dev, adapter_nr); 29362306a36Sopenharmony_ci if (ret < 0) { 29462306a36Sopenharmony_ci dev_err(dev, "%s: dvb_register_adapter() failed: %d\n", 29562306a36Sopenharmony_ci __func__, ret); 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci as102_dev->dvb_dmx.priv = as102_dev; 30062306a36Sopenharmony_ci as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256; 30162306a36Sopenharmony_ci as102_dev->dvb_dmx.feednum = 256; 30262306a36Sopenharmony_ci as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed; 30362306a36Sopenharmony_ci as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING | 30662306a36Sopenharmony_ci DMX_SECTION_FILTERING; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum; 30962306a36Sopenharmony_ci as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx; 31062306a36Sopenharmony_ci as102_dev->dvb_dmxdev.capabilities = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = dvb_dmx_init(&as102_dev->dvb_dmx); 31362306a36Sopenharmony_ci if (ret < 0) { 31462306a36Sopenharmony_ci dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret); 31562306a36Sopenharmony_ci goto edmxinit; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap); 31962306a36Sopenharmony_ci if (ret < 0) { 32062306a36Sopenharmony_ci dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n", 32162306a36Sopenharmony_ci __func__, ret); 32262306a36Sopenharmony_ci goto edmxdinit; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Attach the frontend */ 32662306a36Sopenharmony_ci as102_dev->dvb_fe = dvb_attach(as102_attach, as102_dev->name, 32762306a36Sopenharmony_ci &as102_fe_ops, 32862306a36Sopenharmony_ci &as102_dev->bus_adap, 32962306a36Sopenharmony_ci as102_dev->elna_cfg); 33062306a36Sopenharmony_ci if (!as102_dev->dvb_fe) { 33162306a36Sopenharmony_ci ret = -ENODEV; 33262306a36Sopenharmony_ci dev_err(dev, "%s: as102_attach() failed: %d", 33362306a36Sopenharmony_ci __func__, ret); 33462306a36Sopenharmony_ci goto efereg; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = dvb_register_frontend(&as102_dev->dvb_adap, as102_dev->dvb_fe); 33862306a36Sopenharmony_ci if (ret < 0) { 33962306a36Sopenharmony_ci dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d", 34062306a36Sopenharmony_ci __func__, ret); 34162306a36Sopenharmony_ci goto efereg; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* init bus mutex for token locking */ 34562306a36Sopenharmony_ci mutex_init(&as102_dev->bus_adap.lock); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* init start / stop stream mutex */ 34862306a36Sopenharmony_ci mutex_init(&as102_dev->sem); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * try to load as102 firmware. If firmware upload failed, we'll be 35262306a36Sopenharmony_ci * able to upload it later. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (fw_upload) 35562306a36Sopenharmony_ci try_then_request_module(as102_fw_upload(&as102_dev->bus_adap), 35662306a36Sopenharmony_ci "firmware_class"); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci pr_info("Registered device %s", as102_dev->name); 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciefereg: 36262306a36Sopenharmony_ci dvb_dmxdev_release(&as102_dev->dvb_dmxdev); 36362306a36Sopenharmony_ciedmxdinit: 36462306a36Sopenharmony_ci dvb_dmx_release(&as102_dev->dvb_dmx); 36562306a36Sopenharmony_ciedmxinit: 36662306a36Sopenharmony_ci dvb_unregister_adapter(&as102_dev->dvb_adap); 36762306a36Sopenharmony_ci return ret; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_civoid as102_dvb_unregister(struct as102_dev_t *as102_dev) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci /* unregister as102 frontend */ 37362306a36Sopenharmony_ci dvb_unregister_frontend(as102_dev->dvb_fe); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* detach frontend */ 37662306a36Sopenharmony_ci dvb_frontend_detach(as102_dev->dvb_fe); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* unregister demux device */ 37962306a36Sopenharmony_ci dvb_dmxdev_release(&as102_dev->dvb_dmxdev); 38062306a36Sopenharmony_ci dvb_dmx_release(&as102_dev->dvb_dmx); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* unregister dvb adapter */ 38362306a36Sopenharmony_ci dvb_unregister_adapter(&as102_dev->dvb_adap); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci pr_info("Unregistered device %s", as102_dev->name); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cimodule_usb_driver(as102_usb_driver); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* modinfo details */ 39162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_FULL_NAME); 39262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 39362306a36Sopenharmony_ciMODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); 394