diff options
| -rw-r--r-- | drivers/staging/media/as102/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/staging/media/as102/Makefile | 5 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_drv.c | 356 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_drv.h | 146 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_fe.c | 647 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_fw.c | 236 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_fw.h | 42 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_usb_drv.c | 432 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as102_usb_drv.h | 54 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_cmd.c | 478 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_cmd.h | 540 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_cmd_cfg.c | 239 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_cmd_stream.c | 256 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_handle.h | 58 | ||||
| -rw-r--r-- | drivers/staging/media/as102/as10x_types.h | 198 |
15 files changed, 3694 insertions, 0 deletions
diff --git a/drivers/staging/media/as102/Kconfig b/drivers/staging/media/as102/Kconfig new file mode 100644 index 000000000000..5865029db0f6 --- /dev/null +++ b/drivers/staging/media/as102/Kconfig @@ -0,0 +1,7 @@ +config DVB_AS102 + tristate "Abilis AS102 DVB receiver" + depends on DVB_CORE && USB && I2C && INPUT + help + Choose Y or M here if you have a device containing an AS102 + + To compile this driver as a module, choose M here diff --git a/drivers/staging/media/as102/Makefile b/drivers/staging/media/as102/Makefile new file mode 100644 index 000000000000..c2334c6c952e --- /dev/null +++ b/drivers/staging/media/as102/Makefile @@ -0,0 +1,5 @@ +dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o as102_fe.o as102_usb_drv.o as10x_cmd_cfg.o + +obj-$(CONFIG_DVB_AS102) += dvb-as102.o + +EXTRA_CFLAGS += -DLINUX -DCONFIG_AS102_USB -Idrivers/media/dvb/dvb-core diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c new file mode 100644 index 000000000000..c334bffa36e4 --- /dev/null +++ b/drivers/staging/media/as102/as102_drv.c @@ -0,0 +1,356 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/kref.h> +#include <asm/uaccess.h> +#include <linux/usb.h> + +/* header file for Usb device driver*/ +#include "as102_drv.h" +#include "as102_fw.h" + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) +#include "dvbdev.h" +#else +#warning >>> DVB_CORE not defined !!! <<< +#endif + +int debug = 0; +module_param_named(debug, debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)"); + +int dual_tuner = 0; +module_param_named(dual_tuner, dual_tuner, int, 0644); +MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner configuration (default: off)"); + +static int fw_upload = 1; +module_param_named(fw_upload, fw_upload, int, 0644); +MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)"); + +static int pid_filtering = 0; +module_param_named(pid_filtering, pid_filtering, int, 0644); +MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)"); + +static int ts_auto_disable = 0; +module_param_named(ts_auto_disable, ts_auto_disable, int, 0644); +MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)"); + +int elna_enable = 1; +module_param_named(elna_enable, elna_enable, int, 0644); +MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)"); + +#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +#endif + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) +static void as102_stop_stream(struct as102_dev_t *dev) { + struct as102_bus_adapter_t *bus_adap; + + if (dev != NULL) + bus_adap = &dev->bus_adap; + else + return; + + if (bus_adap->ops->stop_stream != NULL) + bus_adap->ops->stop_stream(dev); + + if (ts_auto_disable) { + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return; + + if (as10x_cmd_stop_streaming(bus_adap) < 0) { + dprintk(debug, "as10x_cmd_stop_streaming failed\n"); + } + + mutex_unlock(&dev->bus_adap.lock); + } +} + +static int as102_start_stream(struct as102_dev_t *dev) { + + struct as102_bus_adapter_t *bus_adap; + int ret = -EFAULT; + + if (dev != NULL) + bus_adap = &dev->bus_adap; + else + return ret; + + if (bus_adap->ops->start_stream != NULL) { + ret = bus_adap->ops->start_stream(dev); + } + + if (ts_auto_disable) { + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EFAULT; + + ret = as10x_cmd_start_streaming(bus_adap); + + mutex_unlock(&dev->bus_adap.lock); + } + + return ret; +} + +static int as10x_pid_filter(struct as102_dev_t *dev, + int index, u16 pid, int onoff) { + + struct as102_bus_adapter_t *bus_adap = &dev->bus_adap; + int ret = -EFAULT; + + ENTER(); + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) { + dprintk(debug, "mutex_lock_interruptible(lock) failed !\n"); + return -EBUSY; + } + + switch(onoff) { + case 0: + ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid); + dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n", + index, pid, ret); + break; + case 1: + { + struct as10x_ts_filter filter; + + filter.type = TS_PID_TYPE_TS; + filter.idx = 0xFF; + filter.pid = pid; + + ret = as10x_cmd_add_PID_filter(bus_adap, &filter); + dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", + index, filter.idx, filter.pid, ret); + break; + } + } + + mutex_unlock(&dev->bus_adap.lock); + + LEAVE(); + return ret; +} + +static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) { + int ret = 0; + struct dvb_demux *demux = dvbdmxfeed->demux; + struct as102_dev_t *as102_dev = demux->priv; + + ENTER(); + + if (mutex_lock_interruptible(&as102_dev->sem)) + return -ERESTARTSYS; + + if (pid_filtering) { + as10x_pid_filter(as102_dev, + dvbdmxfeed->index, dvbdmxfeed->pid, 1); + } + + if (as102_dev->streaming++ == 0) { + ret = as102_start_stream(as102_dev); + } + + mutex_unlock(&as102_dev->sem); + LEAVE(); + return ret; +} + +static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { + struct dvb_demux *demux = dvbdmxfeed->demux; + struct as102_dev_t *as102_dev = demux->priv; + + ENTER(); + + if (mutex_lock_interruptible(&as102_dev->sem)) + return -ERESTARTSYS; + + if (--as102_dev->streaming == 0) { + as102_stop_stream(as102_dev); + } + + if (pid_filtering) { + as10x_pid_filter(as102_dev, + dvbdmxfeed->index, dvbdmxfeed->pid, 0); + } + + mutex_unlock(&as102_dev->sem); + LEAVE(); + return 0; +} +#endif + +int as102_dvb_register(struct as102_dev_t *as102_dev) { + int ret = 0; + ENTER(); + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) + ret = dvb_register_adapter(&as102_dev->dvb_adap, + DEVICE_FULL_NAME, + THIS_MODULE, +#if defined(CONFIG_AS102_USB) + &as102_dev->bus_adap.usb_dev->dev +#elif defined(CONFIG_AS102_SPI) + &as102_dev->bus_adap.spi_dev->dev +#else +#error >>> dvb_register_adapter <<< +#endif +#ifdef DVB_DEFINE_MOD_OPT_ADAPTER_NR + , adapter_nr +#endif + ); + if (ret < 0) { + err("%s: dvb_register_adapter() failed (errno = %d)", + __FUNCTION__, ret); + goto failed; + } + + as102_dev->dvb_dmx.priv = as102_dev; + as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256; + as102_dev->dvb_dmx.feednum = 256; + as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed; + as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed; + + as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING; + + as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum; + as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx; + as102_dev->dvb_dmxdev.capabilities = 0; + + if ((ret = dvb_dmx_init(&as102_dev->dvb_dmx)) < 0) { + err("%s: dvb_dmx_init() failed (errno = %d)", + __FUNCTION__, ret); + goto failed; + } + + ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap); + if (ret < 0) { + err("%s: dvb_dmxdev_init() failed (errno = %d)", + __FUNCTION__, ret); + goto failed; + } + + ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe); + if (ret < 0) { + err("%s: as102_dvb_register_frontend() failed (errno = %d)", + __FUNCTION__, ret); + goto failed; + } +#endif + + /* init bus mutex for token locking */ + mutex_init(&as102_dev->bus_adap.lock); + + /* init start / stop stream mutex */ + mutex_init(&as102_dev->sem); + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) + /* + * try to load as102 firmware. If firmware upload failed, we'll be + * able to upload it later. + */ + if (fw_upload) + try_then_request_module(as102_fw_upload(&as102_dev->bus_adap), + "firmware_class"); +#endif + +failed: + LEAVE(); + /* FIXME: free dvb_XXX */ + return ret; +} + +void as102_dvb_unregister(struct as102_dev_t *as102_dev) { + ENTER(); + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) + /* unregister as102 frontend */ + as102_dvb_unregister_fe(&as102_dev->dvb_fe); + + /* unregister demux device */ + dvb_dmxdev_release(&as102_dev->dvb_dmxdev); + dvb_dmx_release(&as102_dev->dvb_dmx); + + /* unregister dvb adapter */ + dvb_unregister_adapter(&as102_dev->dvb_adap); +#endif + LEAVE(); +} + +static int __init as102_driver_init(void) { + int ret = 0; + + ENTER(); + + /* register this driver with the low level subsystem */ +#if defined(CONFIG_AS102_USB) + ret = usb_register(&as102_usb_driver); + if (ret) + err("usb_register failed (ret = %d)", ret); +#endif +#if defined(CONFIG_AS102_SPI) + ret = spi_register_driver(&as102_spi_driver); + if (ret) + printk(KERN_ERR "spi_register failed (ret = %d)", ret); +#endif + + LEAVE(); + return ret; +} + +/* + * Mandatory function : Adds a special section to the module indicating + * where initialisation function is defined + */ +module_init(as102_driver_init); + +/** + * \brief as102 driver exit point. This function is called when device has + * to be removed. + */ +static void __exit as102_driver_exit(void) { + ENTER(); + /* deregister this driver with the low level bus subsystem */ +#if defined(CONFIG_AS102_USB) + usb_deregister(&as102_usb_driver); +#endif +#if defined(CONFIG_AS102_SPI) + spi_unregister_driver(&as102_spi_driver); +#endif + LEAVE(); +} + +/* + * required function for unload: Adds a special section to the module + * indicating where unload function is defined + */ +module_exit(as102_driver_exit); +/* modinfo details */ +MODULE_DESCRIPTION(DRIVER_FULL_NAME); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h new file mode 100644 index 000000000000..f50bb9f67fce --- /dev/null +++ b/drivers/staging/media/as102/as102_drv.h @@ -0,0 +1,146 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if defined(CONFIG_AS102_USB) +#include <linux/usb.h> +extern struct usb_driver as102_usb_driver; +#endif + +#if defined(CONFIG_AS102_SPI) +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/cdev.h> + +extern struct spi_driver as102_spi_driver; +#endif + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" +#endif + +#define DRIVER_FULL_NAME "Abilis Systems as10x usb driver" +#define DRIVER_NAME "as10x_usb" + +extern int debug; + +#define dprintk(debug, args...) \ + do { if (debug) { \ + printk(KERN_DEBUG "%s: ",__FUNCTION__); \ + printk(args); \ + } } while (0) + +#ifdef TRACE +#define ENTER() printk(">> enter %s\n", __FUNCTION__) +#define LEAVE() printk("<< leave %s\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#define AS102_DEVICE_MAJOR 192 + +#define AS102_USB_BUF_SIZE 512 +#define MAX_STREAM_URB 32 + +#include "as10x_cmd.h" + +#if defined(CONFIG_AS102_USB) +#include "as102_usb_drv.h" +#endif + +#if defined(CONFIG_AS102_SPI) +#include "as10x_spi_drv.h" +#endif + + +struct as102_bus_adapter_t { +#if defined(CONFIG_AS102_USB) + struct usb_device *usb_dev; +#elif defined(CONFIG_AS102_SPI) + struct spi_device *spi_dev; + struct cdev cdev; /* spidev raw device */ + + struct timer_list timer; + struct completion xfer_done; +#endif + /* bus token lock */ + struct mutex lock; + /* low level interface for bus adapter */ + union as10x_bus_token_t { +#if defined(CONFIG_AS102_USB) + /* usb token */ + struct as10x_usb_token_cmd_t usb; +#endif +#if defined(CONFIG_AS102_SPI) + /* spi token */ + struct as10x_spi_token_cmd_t spi; +#endif + } token; + + /* token cmd xfer id */ + uint16_t cmd_xid; + + /* as10x command and response for dvb interface*/ + struct as10x_cmd_t *cmd, *rsp; + + /* bus adapter private ops callback */ + struct as102_priv_ops_t *ops; +}; + +struct as102_dev_t { + struct as102_bus_adapter_t bus_adap; + struct list_head device_entry; + struct kref kref; + unsigned long minor; + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) + struct dvb_adapter dvb_adap; + struct dvb_frontend dvb_fe; + struct dvb_demux dvb_dmx; + struct dmxdev dvb_dmxdev; +#endif + + /* demodulator stats */ + struct as10x_demod_stats demod_stats; + /* signal strength */ + uint16_t signal_strength; + /* bit error rate */ + uint32_t ber; + + /* timer handle to trig ts stream download */ + struct timer_list timer_handle; + + struct mutex sem; + dma_addr_t dma_addr; + void *stream; + int streaming; + struct urb *stream_urb[MAX_STREAM_URB]; +}; + +int as102_dvb_register(struct as102_dev_t *dev); +void as102_dvb_unregister(struct as102_dev_t *dev); + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) +int as102_dvb_register_fe(struct as102_dev_t *dev, struct dvb_frontend *fe); +int as102_dvb_unregister_fe(struct dvb_frontend *dev); +#endif + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c new file mode 100644 index 000000000000..3e6f497aed51 --- /dev/null +++ b/drivers/staging/media/as102/as102_fe.c @@ -0,0 +1,647 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/version.h> + +#include "as102_drv.h" +#include "as10x_types.h" +#include "as10x_cmd.h" + +extern int elna_enable; + +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) +static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst, + struct as10x_tps *src); + +static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst, + struct dvb_frontend_parameters *src); + +static void as102_fe_release(struct dvb_frontend *fe) { + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return; + + /* send abilis command: TURN_OFF */ + as10x_cmd_turn_off(&dev->bus_adap); + + mutex_unlock(&dev->bus_adap.lock); +#endif + + /* release frontend callback ops */ + memset(&fe->ops, 0, sizeof(struct dvb_frontend_ops)); + + /* flush statistics */ + memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); + dev->signal_strength = 0; + dev->ber = -1; + + /* reset tuner private data */ +/* fe->tuner_priv = NULL; */ + + LEAVE(); +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) +static int as102_fe_init(struct dvb_frontend *fe) { + int ret = 0; + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EBUSY; + + if (elna_enable) + ret = as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0); + + /* send abilis command: TURN_ON */ + ret = as10x_cmd_turn_on(&dev->bus_adap); + + mutex_unlock(&dev->bus_adap.lock); + + LEAVE(); + return (ret < 0) ? -EINVAL : 0; +} +#endif + +static int as102_fe_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { + int ret = 0; + struct as102_dev_t *dev; + struct as10x_tune_args tune_args = { 0 }; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EBUSY; + + as102_fe_copy_tune_parameters(&tune_args, params); + + /* send abilis command: SET_TUNE */ + ret = as10x_cmd_set_tune(&dev->bus_adap, &tune_args); + if(ret != 0) { + dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret); + } + + mutex_unlock(&dev->bus_adap.lock); + + LEAVE(); + return (ret < 0) ? -EINVAL : 0; +} + +static int as102_fe_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) { + int ret = 0; + struct as102_dev_t *dev; + struct as10x_tps tps = { 0 }; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -EINVAL; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EBUSY; + + /* send abilis command: GET_TPS */ + ret = as10x_cmd_get_tps(&dev->bus_adap, &tps); + + if (ret == 0) + as10x_fe_copy_tps_parameters(p, &tps); + + mutex_unlock(&dev->bus_adap.lock); + + LEAVE(); + return (ret < 0) ? -EINVAL : 0; +} + +static int as102_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *settings) { + ENTER(); + +#if 0 + dprintk(debug, "step_size = %d\n", settings->step_size); + dprintk(debug, "max_drift = %d\n", settings->max_drift); + dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms, 1000); +#endif + + settings->min_delay_ms = 1000; + + LEAVE(); + return 0; +} + + +static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) { + int ret = 0; + struct as102_dev_t *dev; + struct as10x_tune_status tstate = { 0 }; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EBUSY; + + /* send abilis command: GET_TUNE_STATUS */ + ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate); + if (ret < 0) { + dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n", ret); + goto out; + } + + dev->signal_strength = tstate.signal_strength; + dev->ber = tstate.BER; + + switch(tstate.tune_state) { + case TUNE_STATUS_SIGNAL_DVB_OK: + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER; + break; + case TUNE_STATUS_STREAM_DETECTED: + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_SYNC; + break; + case TUNE_STATUS_STREAM_TUNED: + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_SYNC | + FE_HAS_LOCK; + break; + default: + *status = TUNE_STATUS_NOT_TUNED; + } + + dprintk(debug, "tuner status: 0x%02x , strength %d , per: %d , ber: %d\n", + tstate.tune_state, tstate.signal_strength, + tstate.PER, tstate.BER); + + if (*status & FE_HAS_LOCK) { + if (as10x_cmd_get_demod_stats(&dev->bus_adap, + (struct as10x_demod_stats *) &dev->demod_stats) < 0) { + memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); + dprintk(debug, "as10x_cmd_get_demod_stats failed (probably not tuned)\n"); + } else { + dprintk(debug, "demod status: fc: 0x%08x , bad fc: 0x%08x , bytes corrected: 0x%08x , MER: 0x%04x\n", + dev->demod_stats.frame_count, + dev->demod_stats.bad_frame_count, + dev->demod_stats.bytes_fixed_by_rs, + dev->demod_stats.mer); + } + } else { + memset(&dev->demod_stats, 0, sizeof(dev->demod_stats)); + } + +out: + mutex_unlock(&dev->bus_adap.lock); + LEAVE(); + return ret; +} + +/* + * Note: + * - in AS102 SNR=MER + * - the SNR will be returned in linear terms, i.e. not in dB + * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB + * - the accuracy is >2dB for SNR values outside this range + */ +static int as102_fe_read_snr(struct dvb_frontend* fe, u16* snr) { + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + *snr = dev->demod_stats.mer; + + LEAVE(); + return 0; +} + +static int as102_fe_read_ber(struct dvb_frontend* fe, u32* ber) { + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + *ber = dev->ber; + + LEAVE(); + return 0; +} + +static int as102_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) { + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + *strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2); + + LEAVE(); + return 0; +} + +static int as102_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { + struct as102_dev_t *dev; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + if (dev->demod_stats.has_started) + *ucblocks = dev->demod_stats.bad_frame_count; + else + *ucblocks = 0; + + LEAVE(); + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) +static int as102_fe_ts_bus_ctrl(struct dvb_frontend* fe, int acquire) { + struct as102_dev_t *dev; + int ret; + + ENTER(); + + if ((dev = (struct as102_dev_t *) fe->tuner_priv) == NULL) + return -ENODEV; + + if (mutex_lock_interruptible(&dev->bus_adap.lock)) + return -EBUSY; + + if (acquire) { + if (elna_enable) + as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0); + + ret = as10x_cmd_turn_on(&dev->bus_adap); + } else { + ret = as10x_cmd_turn_off(&dev->bus_adap); + } + + mutex_unlock(&dev->bus_adap.lock); + + LEAVE(); + return ret; +} +#endif + +static struct dvb_frontend_ops as102_fe_ops = { + .info = { + .name = DEVICE_FULL_NAME, + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_INVERSION_AUTO + | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 + | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO + | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK + | FE_CAN_QAM_AUTO + | FE_CAN_TRANSMISSION_MODE_AUTO + | FE_CAN_GUARD_INTERVAL_AUTO + | FE_CAN_HIERARCHY_AUTO + | FE_CAN_RECOVER + | FE_CAN_MUTE_TS + }, + + .set_frontend = as102_fe_set_frontend, + .get_frontend = as102_fe_get_frontend, + .get_tune_settings = as102_fe_get_tune_settings, + + + .read_status = as102_fe_read_status, + .read_snr = as102_fe_read_snr, + .read_ber = as102_fe_read_ber, + .read_signal_strength = as102_fe_read_signal_strength, + .read_ucblocks = as102_fe_read_ucblocks, + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) + .ts_bus_ctrl = as102_fe_ts_bus_ctrl, +#else + .release = as102_fe_release, + .init = as102_fe_init, +#endif +}; + +int as102_dvb_unregister_fe(struct dvb_frontend *fe) { + + /* unregister frontend */ + dvb_unregister_frontend(fe); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) + /* detach frontend */ + dvb_frontend_detach(fe); +#endif + return 0; +} + +int as102_dvb_register_fe(struct as102_dev_t *as102_dev, struct dvb_frontend *dvb_fe) { + int errno; + struct dvb_adapter *dvb_adap; + + if(as102_dev == NULL) + return -EINVAL; + + /* extract dvb_adapter */ + dvb_adap = &as102_dev->dvb_adap; + + /* init frontend callback ops */ + memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops)); + + /* register dbvb frontend */ + errno = dvb_register_frontend(dvb_adap, dvb_fe); + if(errno == 0) + dvb_fe->tuner_priv = as102_dev; + + return errno; +} + +static void as10x_fe_copy_tps_parameters(struct dvb_frontend_parameters *dst, + struct as10x_tps *as10x_tps) { + + struct dvb_ofdm_parameters *fe_tps = &dst->u.ofdm; + + /* extract consteallation */ + switch(as10x_tps->constellation) { + case CONST_QPSK: + fe_tps->constellation = QPSK; + break; + case CONST_QAM16: + fe_tps->constellation = QAM_16; + break; + case CONST_QAM64: + fe_tps->constellation = QAM_64; + break; + } + + /* extract hierarchy */ + switch(as10x_tps->hierarchy) { + case HIER_NONE: + fe_tps->hierarchy_information = HIERARCHY_NONE; + break; + case HIER_ALPHA_1: + fe_tps->hierarchy_information = HIERARCHY_1; + break; + case HIER_ALPHA_2: + fe_tps->hierarchy_information = HIERARCHY_2; + break; + case HIER_ALPHA_4: + fe_tps->hierarchy_information = HIERARCHY_4; + break; + } + + /* extract code rate HP */ + switch(as10x_tps->code_rate_HP) { + case CODE_RATE_1_2: + fe_tps->code_rate_HP = FEC_1_2; + break; + case CODE_RATE_2_3: + fe_tps->code_rate_HP = FEC_2_3; + break; + case CODE_RATE_3_4: + fe_tps->code_rate_HP = FEC_3_4; + break; + case CODE_RATE_5_6: + fe_tps->code_rate_HP = FEC_5_6; + break; + case CODE_RATE_7_8: + fe_tps->code_rate_HP = FEC_7_8; + break; + } + + /* extract code rate LP */ + switch(as10x_tps->code_rate_LP) { + case CODE_RATE_1_2: + fe_tps->code_rate_LP = FEC_1_2; + break; + case CODE_RATE_2_3: + fe_tps->code_rate_LP = FEC_2_3; + break; + case CODE_RATE_3_4: + fe_tps->code_rate_LP = FEC_3_4; + break; + case CODE_RATE_5_6: + fe_tps->code_rate_LP = FEC_5_6; + break; + case CODE_RATE_7_8: + fe_tps->code_rate_LP = FEC_7_8; + break; + } + + /* extract guard interval */ + switch(as10x_tps->guard_interval) { + case GUARD_INT_1_32: + fe_tps->guard_interval = GUARD_INTERVAL_1_32; + break; + case GUARD_INT_1_16: + fe_tps->guard_interval = GUARD_INTERVAL_1_16; + break; + case GUARD_INT_1_8: + fe_tps->guard_interval = GUARD_INTERVAL_1_8; + break; + case GUARD_INT_1_4: + fe_tps->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + /* extract transmission mode */ + switch(as10x_tps->transmission_mode) { + case TRANS_MODE_2K: + fe_tps->transmission_mode = TRANSMISSION_MODE_2K; + break; + case TRANS_MODE_8K: + fe_tps->transmission_mode = TRANSMISSION_MODE_8K; + break; + } +} + +static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg) { + uint8_t c; + + switch(arg) { + case FEC_1_2: + c = CODE_RATE_1_2; + break; + case FEC_2_3: + c = CODE_RATE_2_3; + break; + case FEC_3_4: + c = CODE_RATE_3_4; + break; + case FEC_5_6: + c = CODE_RATE_5_6; + break; + case FEC_7_8: + c = CODE_RATE_7_8; + break; + default: + c = CODE_RATE_UNKNOWN; + break; + } + + return c; +} + +static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args, + struct dvb_frontend_parameters *params) { + + /* set frequency */ + tune_args->freq = params->frequency / 1000; + + /* fix interleaving_mode */ + tune_args->interleaving_mode = INTLV_NATIVE; + + switch(params->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: + tune_args->bandwidth = BW_8_MHZ; + break; + case BANDWIDTH_7_MHZ: |
