brintos

brintos / linux-shallow public Read only

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