From 41b44e0418112e694f9b7beb8088efbb8c9c0053 Mon Sep 17 00:00:00 2001 From: Pierrick Hascoet Date: Mon, 31 Oct 2011 12:24:39 -0300 Subject: [media] staging: as102: Initial import from Abilis Changes by Devin Heitmueller: Import the original Abilis Systems as102 driver. The source is unmodified, with the only changes I've made so far were that I created a Kconfig and Makefile so that the code builds in a standard v4l-dvb tree. This driver requires firmware (which Abilis has provided with redistribution terms which will allow it to be bundled in the Linux distributions). The firmware can be downloaded from here: Thanks to Rainer Miethling from PCTV Systems for working to get the driver released (for use with the PCTV 74e) and Pierrick Hascoet from Abilis for authoring the driver. Changes by Piotr Chmura: - moved the driver from media/dvb to staging/media - removed Makefile/Kconfig - compilation fails in current tree [snjw23@gmail.com: edited changelog] Signed-off-by: Pierrick Hascoet Signed-off-by: Devin Heitmueller Signed-off-by: Piotr Chmura Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/Kconfig | 7 + drivers/staging/media/as102/Makefile | 5 + drivers/staging/media/as102/as102_drv.c | 356 ++++++++++++++ drivers/staging/media/as102/as102_drv.h | 146 ++++++ drivers/staging/media/as102/as102_fe.c | 647 +++++++++++++++++++++++++ drivers/staging/media/as102/as102_fw.c | 236 +++++++++ drivers/staging/media/as102/as102_fw.h | 42 ++ drivers/staging/media/as102/as102_usb_drv.c | 432 +++++++++++++++++ drivers/staging/media/as102/as102_usb_drv.h | 54 +++ drivers/staging/media/as102/as10x_cmd.c | 478 ++++++++++++++++++ drivers/staging/media/as102/as10x_cmd.h | 540 +++++++++++++++++++++ drivers/staging/media/as102/as10x_cmd_cfg.c | 239 +++++++++ drivers/staging/media/as102/as10x_cmd_stream.c | 256 ++++++++++ drivers/staging/media/as102/as10x_handle.h | 58 +++ drivers/staging/media/as102/as10x_types.h | 198 ++++++++ 15 files changed, 3694 insertions(+) create mode 100644 drivers/staging/media/as102/Kconfig create mode 100644 drivers/staging/media/as102/Makefile create mode 100644 drivers/staging/media/as102/as102_drv.c create mode 100644 drivers/staging/media/as102/as102_drv.h create mode 100644 drivers/staging/media/as102/as102_fe.c create mode 100644 drivers/staging/media/as102/as102_fw.c create mode 100644 drivers/staging/media/as102/as102_fw.h create mode 100644 drivers/staging/media/as102/as102_usb_drv.c create mode 100644 drivers/staging/media/as102/as102_usb_drv.h create mode 100644 drivers/staging/media/as102/as10x_cmd.c create mode 100644 drivers/staging/media/as102/as10x_cmd.h create mode 100644 drivers/staging/media/as102/as10x_cmd_cfg.c create mode 100644 drivers/staging/media/as102/as10x_cmd_stream.c create mode 100644 drivers/staging/media/as102/as10x_handle.h create mode 100644 drivers/staging/media/as102/as10x_types.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/as102/Kconfig b/drivers/staging/media/as102/Kconfig new file mode 100644 index 00000000000..5865029db0f --- /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 00000000000..c2334c6c952 --- /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 00000000000..c334bffa36e --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); + +/* 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 00000000000..f50bb9f67fc --- /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 + * + * 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 +extern struct usb_driver as102_usb_driver; +#endif + +#if defined(CONFIG_AS102_SPI) +#include +#include +#include + +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 00000000000..3e6f497aed5 --- /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 + * + * 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 + +#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: + tune_args->bandwidth = BW_7_MHZ; + break; + case BANDWIDTH_6_MHZ: + tune_args->bandwidth = BW_6_MHZ; + break; + default: + tune_args->bandwidth = BW_8_MHZ; + } + + switch(params->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_32: + tune_args->guard_interval = GUARD_INT_1_32; + break; + case GUARD_INTERVAL_1_16: + tune_args->guard_interval = GUARD_INT_1_16; + break; + case GUARD_INTERVAL_1_8: + tune_args->guard_interval = GUARD_INT_1_8; + break; + case GUARD_INTERVAL_1_4: + tune_args->guard_interval = GUARD_INT_1_4; + break; + case GUARD_INTERVAL_AUTO: + default: + tune_args->guard_interval = GUARD_UNKNOWN; + break; + } + + switch(params->u.ofdm.constellation) { + case QPSK: + tune_args->constellation = CONST_QPSK; + break; + case QAM_16: + tune_args->constellation = CONST_QAM16; + break; + case QAM_64: + tune_args->constellation = CONST_QAM64; + break; + default: + tune_args->constellation = CONST_UNKNOWN; + break; + } + + switch(params->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_2K: + tune_args->transmission_mode = TRANS_MODE_2K; + break; + case TRANSMISSION_MODE_8K: + tune_args->transmission_mode = TRANS_MODE_8K; + break; + default: + tune_args->transmission_mode = TRANS_MODE_UNKNOWN; + } + + switch(params->u.ofdm.hierarchy_information) { + case HIERARCHY_NONE: + tune_args->hierarchy = HIER_NONE; + break; + case HIERARCHY_1: + tune_args->hierarchy = HIER_ALPHA_1; + break; + case HIERARCHY_2: + tune_args->hierarchy = HIER_ALPHA_2; + break; + case HIERARCHY_4: + tune_args->hierarchy = HIER_ALPHA_4; + break; + case HIERARCHY_AUTO: + tune_args->hierarchy = HIER_UNKNOWN; + break; + } + + dprintk(debug, "tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n", + params->frequency, + tune_args->bandwidth, + tune_args->guard_interval); + + /* + * Detect a hierarchy selection + * if HP/LP are both set to FEC_NONE, HP will be selected. + */ + if ((tune_args->hierarchy != HIER_NONE) && + ((params->u.ofdm.code_rate_LP == FEC_NONE) || + (params->u.ofdm.code_rate_HP == FEC_NONE))) { + + if (params->u.ofdm.code_rate_LP == FEC_NONE) { + tune_args->hier_select = HIER_HIGH_PRIORITY; + tune_args->code_rate = + as102_fe_get_code_rate(params->u.ofdm.code_rate_HP); + } + + if (params->u.ofdm.code_rate_HP == FEC_NONE) { + tune_args->hier_select = HIER_LOW_PRIORITY; + tune_args->code_rate = + as102_fe_get_code_rate(params->u.ofdm.code_rate_LP); + } + + dprintk(debug, "\thierarchy: 0x%02x " + "selected: %s code_rate_%s: 0x%02x\n", + tune_args->hierarchy, + tune_args->hier_select == HIER_HIGH_PRIORITY ? "HP" : "LP", + tune_args->hier_select == HIER_HIGH_PRIORITY ? "HP" : "LP", + tune_args->code_rate); + } else { + tune_args->code_rate = as102_fe_get_code_rate(params->u.ofdm.code_rate_HP); + } +} +#endif + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_fw.c b/drivers/staging/media/as102/as102_fw.c new file mode 100644 index 00000000000..d921a6fd695 --- /dev/null +++ b/drivers/staging/media/as102/as102_fw.c @@ -0,0 +1,236 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet + * + * 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 +#include +#include +#include +#include + +#include "as102_drv.h" +#include "as102_fw.h" + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +char as102_st_fw1[] = "as102_data1_st.hex"; +char as102_st_fw2[] = "as102_data2_st.hex"; +char as102_dt_fw1[] = "as102_data1_dt.hex"; +char as102_dt_fw2[] = "as102_data2_dt.hex"; + +static unsigned char atohx(unsigned char *dst, char *src) { + unsigned char value = 0; + + char msb = tolower(*src) - '0'; + char lsb = tolower(*(src +1)) - '0'; + + if (msb > 9 ) + msb -= 7; + if (lsb > 9 ) + lsb -= 7; + + *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); + return value; +} + +/* + * Parse INTEL HEX firmware file to extract address and data. + */ +static int parse_hex_line(unsigned char *fw_data, unsigned char *addr, + unsigned char *data, int *dataLength, + unsigned char *addr_has_changed) { + + int count = 0; + unsigned char *src, dst; + + if (*fw_data++ != ':') { + printk(KERN_ERR "invalid firmware file\n"); + return -EFAULT; + } + + /* locate end of line */ + for (src=fw_data; *src != '\n'; src += 2) { + atohx(&dst, src); + /* parse line to split addr / data */ + switch (count) { + case 0: + *dataLength = dst; + break; + case 1: + addr[2] = dst; + break; + case 2: + addr[3] = dst; + break; + case 3: + /* check if data is an address */ + if (dst == 0x04) + *addr_has_changed = 1; + else + *addr_has_changed = 0; + break; + case 4: + case 5: + if (*addr_has_changed) { + addr[(count - 4)] = dst; + } else { + data[(count - 4)] = dst; + } + break; + default: + data[(count - 4)] = dst; + break; + } + count++; + } + + /* return read value + ':' + '\n' */ + return ((count * 2) + 2); +} + +static int as102_firmware_upload(struct as102_bus_adapter_t *bus_adap, + unsigned char *cmd, + const struct firmware *firmware) { + + struct as10x_fw_pkt_t fw_pkt; + int total_read_bytes = 0, errno = 0; + unsigned char addr_has_changed = 0; + + ENTER(); + + for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { + int read_bytes = 0, data_len = 0; + + /* parse intel hex line */ + read_bytes = parse_hex_line( + (u8 *) (firmware->data + total_read_bytes), + fw_pkt.raw.address, + fw_pkt.raw.data, + &data_len, + &addr_has_changed); + + if (read_bytes <= 0) { + goto error; + } + + /* detect the end of file */ + if ((total_read_bytes += read_bytes) == firmware->size) { + fw_pkt.u.request[0] = 0x00; + fw_pkt.u.request[1] = 0x03; + + /* send EOF command */ + if ((errno = bus_adap->ops->upload_fw_pkt(bus_adap,(uint8_t *) &fw_pkt, 2, 0)) < 0) + goto error; + } else { + if (!addr_has_changed) { + /* prepare command to send */ + fw_pkt.u.request[0] = 0x00; + fw_pkt.u.request[1] = 0x01; + + data_len += sizeof(fw_pkt.u.request); + data_len += sizeof(fw_pkt.raw.address); + + /* send cmd to device */ + if ((errno = bus_adap->ops->upload_fw_pkt(bus_adap, (uint8_t *) &fw_pkt, data_len, 0)) < 0) + goto error; + } + } + } +error: + LEAVE(); + return (errno == 0) ? total_read_bytes : errno; +} + +int as102_fw_upload(struct as102_bus_adapter_t *bus_adap) { + int errno = -EFAULT; + const struct firmware *firmware; + unsigned char *cmd_buf = NULL; + char *fw1, *fw2; + +#if defined(CONFIG_AS102_USB) + struct usb_device *dev = bus_adap->usb_dev; +#endif +#if defined(CONFIG_AS102_SPI) + struct spi_device *dev = bus_adap->spi_dev; +#endif + ENTER(); + + /* select fw file to upload */ + if (dual_tuner) { + fw1 = as102_dt_fw1; + fw2 = as102_dt_fw2; + } else { + fw1 = as102_st_fw1; + fw2 = as102_st_fw2; + } + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) + /* allocate buffer to store firmware upload command and data */ + if ((cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL)) == NULL) { + errno = -ENOMEM; + goto error; + } + + /* request kernel to locate firmware file: part1 */ + if ((errno = request_firmware(&firmware, fw1, &dev->dev)) < 0) { + printk(KERN_ERR "%s: unable to locate firmware file: %s\n", + DRIVER_NAME, fw1); + goto error; + } + + /* initiate firmware upload */ + if ((errno = as102_firmware_upload(bus_adap, cmd_buf, firmware)) < 0) { + printk(KERN_ERR "%s: error during firmware upload part1\n", + DRIVER_NAME); + goto error; + } + + printk(KERN_INFO "%s: fimrware: %s loaded with success\n", + DRIVER_NAME, fw1); + release_firmware(firmware); + + /* wait for boot to complete */ + mdelay(100); + + /* request kernel to locate firmware file: part2 */ + if ((errno = request_firmware(&firmware, fw2, &dev->dev)) < 0) { + printk(KERN_ERR "%s: unable to locate firmware file: %s\n", + DRIVER_NAME, fw2); + goto error; + } + + /* initiate firmware upload */ + if ((errno = as102_firmware_upload(bus_adap, cmd_buf, firmware)) < 0) { + printk(KERN_ERR "%s: error during firmware upload part2\n", + DRIVER_NAME); + goto error; + } + + printk(KERN_INFO "%s: fimrware: %s loaded with success\n", + DRIVER_NAME, fw2); +error: + /* free data buffer */ + kfree(cmd_buf); + /* release firmware if needed */ + if (firmware != NULL) + release_firmware(firmware); +#endif + LEAVE(); + return errno; +} +#endif + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_fw.h b/drivers/staging/media/as102/as102_fw.h new file mode 100644 index 00000000000..27e5347e2e1 --- /dev/null +++ b/drivers/staging/media/as102/as102_fw.h @@ -0,0 +1,42 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet + * + * 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. + */ +#define MAX_FW_PKT_SIZE 64 + +extern int dual_tuner; + +#pragma pack(1) +struct as10x_raw_fw_pkt { + unsigned char address[4]; + unsigned char data[MAX_FW_PKT_SIZE - 6]; +}; + +struct as10x_fw_pkt_t { + union { + unsigned char request[2]; + unsigned char length[2]; + } u; + struct as10x_raw_fw_pkt raw; +}; +#pragma pack() + +#ifdef __KERNEL__ +int as102_fw_upload(struct as102_bus_adapter_t *bus_adap); +#endif + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c new file mode 100644 index 00000000000..ee993960731 --- /dev/null +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -0,0 +1,432 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet + * + * 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 +#include +#include +#include +#include + +#include "as102_drv.h" +#include "as102_usb_drv.h" +#include "as102_fw.h" + +static void as102_usb_disconnect(struct usb_interface *interface); +static int as102_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id); + +static int as102_usb_start_stream(struct as102_dev_t *dev); +static void as102_usb_stop_stream(struct as102_dev_t *dev); + +static int as102_open(struct inode *inode, struct file *file); +static int as102_release(struct inode *inode, struct file *file); + +static struct usb_device_id as102_usb_id_table[] = { + { USB_DEVICE(AS102_USB_DEVICE_VENDOR_ID, AS102_USB_DEVICE_PID_0001) }, + { USB_DEVICE(PCTV_74E_USB_VID, PCTV_74E_USB_PID) }, + { } /* Terminating entry */ +}; + +struct usb_driver as102_usb_driver = { + .name = DRIVER_FULL_NAME, + .probe = as102_usb_probe, + .disconnect = as102_usb_disconnect, + .id_table = as102_usb_id_table +}; + +struct file_operations as102_dev_fops = { + .owner = THIS_MODULE, + .open = as102_open, + .release = as102_release, +}; + +static struct usb_class_driver as102_usb_class_driver = { + .name = "aton2-%d", + .fops = &as102_dev_fops, + .minor_base = AS102_DEVICE_MAJOR, +}; + +static int as102_usb_xfer_cmd(struct as102_bus_adapter_t *bus_adap, + unsigned char *send_buf, int send_buf_len, + unsigned char *recv_buf, int recv_buf_len) { + + int ret = 0; + ENTER(); + + if(send_buf != NULL) { + ret = usb_control_msg(bus_adap->usb_dev, + usb_sndctrlpipe(bus_adap->usb_dev, 0), + AS102_USB_DEVICE_TX_CTRL_CMD, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + bus_adap->cmd_xid, /* value */ + 0, /* index */ + send_buf, send_buf_len, + USB_CTRL_SET_TIMEOUT /* 200 */); + if(ret < 0) { + dprintk(debug, "usb_control_msg(send) failed, err %i\n", + ret); + return ret; + } + + if(ret != send_buf_len) { + dprintk(debug, "only wrote %d of %d bytes\n", + ret, send_buf_len); + return -1; + } + } + + if(recv_buf != NULL) { +#ifdef TRACE + dprintk(debug, "want to read: %d bytes\n", recv_buf_len); +#endif + ret = usb_control_msg(bus_adap->usb_dev, + usb_rcvctrlpipe(bus_adap->usb_dev, 0), + AS102_USB_DEVICE_RX_CTRL_CMD, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + bus_adap->cmd_xid, /* value */ + 0, /* index */ + recv_buf, recv_buf_len, + USB_CTRL_GET_TIMEOUT /* 200 */); + if(ret < 0) { + dprintk(debug, "usb_control_msg(recv) failed, err %i\n", + ret); + return ret; + } +#ifdef TRACE + dprintk(debug, "read %d bytes\n", recv_buf_len); +#endif + } + + LEAVE(); + return ret; +} + +static int as102_send_ep1(struct as102_bus_adapter_t *bus_adap, + unsigned char *send_buf, + int send_buf_len, + int swap32) { + + int ret = 0, actual_len; + + ret = usb_bulk_msg(bus_adap->usb_dev, usb_sndbulkpipe(bus_adap->usb_dev, 1), + send_buf, send_buf_len, &actual_len, 200); + if(ret) { + dprintk(debug, "usb_bulk_msg(send) failed, err %i\n", ret); + return ret; + } + + if(actual_len != send_buf_len) { + dprintk(debug, "only wrote %d of %d bytes\n", + actual_len, send_buf_len); + return -1; + } + return ret ? ret : actual_len; +} + +static int as102_read_ep2(struct as102_bus_adapter_t *bus_adap, + unsigned char *recv_buf, int recv_buf_len) { + + int ret = 0, actual_len; + + if(recv_buf == NULL) + return -EINVAL; + + ret = usb_bulk_msg(bus_adap->usb_dev, usb_rcvbulkpipe(bus_adap->usb_dev, 2), + recv_buf, recv_buf_len, &actual_len, 200); + if(ret) { + dprintk(debug, "usb_bulk_msg(recv) failed, err %i\n", ret); + return ret; + } + + if(actual_len != recv_buf_len) { + dprintk(debug, "only read %d of %d bytes\n", + actual_len, recv_buf_len); + return -1; + } + return ret ? ret : actual_len; +} + +struct as102_priv_ops_t as102_priv_ops = { + .upload_fw_pkt = as102_send_ep1, + .xfer_cmd = as102_usb_xfer_cmd, + .as102_read_ep2 = as102_read_ep2, + .start_stream = as102_usb_start_stream, + .stop_stream = as102_usb_stop_stream, +}; + +static int as102_submit_urb_stream(struct as102_dev_t *dev, struct urb *urb) { + int err; + + usb_fill_bulk_urb(urb, + dev->bus_adap.usb_dev, + usb_rcvbulkpipe(dev->bus_adap.usb_dev, 0x2), + urb->transfer_buffer, + AS102_USB_BUF_SIZE, + as102_urb_stream_irq, + dev); + + if ((err = usb_submit_urb(urb, GFP_ATOMIC))) + dprintk(debug, "%s: usb_submit_urb failed\n", __FUNCTION__); + + return err; +} + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)) +void as102_urb_stream_irq(struct urb *urb, struct pt_regs *regs) +#else +void as102_urb_stream_irq(struct urb *urb) +#endif +{ + struct as102_dev_t *as102_dev = urb->context; + + if (urb->actual_length > 0) { +#if defined(CONFIG_DVB_CORE) || defined(CONFIG_DVB_CORE_MODULE) + dvb_dmx_swfilter(&as102_dev->dvb_dmx, + urb->transfer_buffer, + urb->actual_length); +#else + /* do nothing ? */ +#endif + } else { + if(urb->actual_length == 0) + memset(urb->transfer_buffer, 0, AS102_USB_BUF_SIZE); + } + + /* is not stopped, re-submit urb */ + if (as102_dev->streaming) + as102_submit_urb_stream(as102_dev, urb); +} + +static void as102_free_usb_stream_buffer(struct as102_dev_t *dev) { + int i; + + ENTER(); + + for (i = 0; i < MAX_STREAM_URB; i++) + usb_free_urb(dev->stream_urb[i]); + + usb_buffer_free(dev->bus_adap.usb_dev, + MAX_STREAM_URB * AS102_USB_BUF_SIZE, + dev->stream, + dev->dma_addr); + LEAVE(); +} + +static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev) { + int i, ret = 0; + + ENTER(); + + dev->stream = usb_buffer_alloc(dev->bus_adap.usb_dev, + MAX_STREAM_URB * AS102_USB_BUF_SIZE, + GFP_KERNEL, + &dev->dma_addr); + if (!dev->stream) { + dprintk(debug, "%s: usb_buffer_alloc failed\n", __FUNCTION__); + return -ENOMEM; + } + + memset(dev->stream, 0, MAX_STREAM_URB * AS102_USB_BUF_SIZE); + + /* init urb buffers */ + for (i = 0; i < MAX_STREAM_URB; i++) { + struct urb *urb; + + if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) { + dprintk(debug, "%s: usb_alloc_urb failed\n", __FUNCTION__); + as102_free_usb_stream_buffer(dev); + return -ENOMEM; + } + + urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE); + urb->transfer_buffer_length = AS102_USB_BUF_SIZE; + + dev->stream_urb[i] = urb; + } + LEAVE(); + return ret; +} + +static void as102_usb_stop_stream(struct as102_dev_t *dev) { + int i; + + for (i = 0; i < MAX_STREAM_URB; i++) + usb_kill_urb(dev->stream_urb[i]); +} + +static int as102_usb_start_stream(struct as102_dev_t *dev) { + int i, ret = 0; + + for (i = 0; i < MAX_STREAM_URB; i++) { + if ((ret = as102_submit_urb_stream(dev, dev->stream_urb[i]))) { + as102_usb_stop_stream(dev); + return ret; + } + } + + return 0; +} + +static void as102_usb_release(struct kref *kref) { + struct as102_dev_t *as102_dev; + + ENTER(); + + as102_dev = container_of(kref, struct as102_dev_t, kref); + if (as102_dev != NULL) { + usb_put_dev(as102_dev->bus_adap.usb_dev); + kfree(as102_dev); + } + + LEAVE(); +} + +static void as102_usb_disconnect(struct usb_interface *intf) { + struct as102_dev_t *as102_dev; + + ENTER(); + + /* extract as102_dev_t from usb_device private data */ + as102_dev = usb_get_intfdata(intf); + + /* unregister dvb layer */ + as102_dvb_unregister(as102_dev); + + /* free usb buffers */ + as102_free_usb_stream_buffer(as102_dev); + + usb_set_intfdata(intf, NULL); + + /* usb unregister device */ + usb_deregister_dev(intf, &as102_usb_class_driver); + + /* decrement usage counter */ + kref_put(&as102_dev->kref, as102_usb_release); + + printk(KERN_INFO "%s: device has been disconnected\n", DRIVER_NAME); + + LEAVE(); +} + +static int as102_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) { + int ret; + struct as102_dev_t *as102_dev; + + ENTER(); + + if(!(as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL))) { + err("%s: kzalloc failed", __FUNCTION__); + return -ENOMEM; + } + + /* set private callback functions */ + as102_dev->bus_adap.ops = &as102_priv_ops; + + /* init cmd token for usb bus */ + as102_dev->bus_adap.cmd = &as102_dev->bus_adap.token.usb.c; + as102_dev->bus_adap.rsp = &as102_dev->bus_adap.token.usb.r; + + /* init kernel device reference */ + kref_init(&as102_dev->kref); + + /* store as102 device to usb_device private data */ + usb_set_intfdata(intf, (void *) as102_dev); + + /* store in as102 device the usb_device pointer */ + as102_dev->bus_adap.usb_dev = usb_get_dev(interface_to_usbdev(intf)); + + /* we can register the device now, as it is ready */ + if((ret = usb_register_dev(intf, &as102_usb_class_driver)) < 0) {; + /* something prevented us from registering this driver */ + err("%s: usb_register_dev() failed (errno = %d)", + __FUNCTION__, ret); + goto failed; + } + + printk(KERN_INFO "%s: device has been detected\n", DRIVER_NAME); + + /* request buffer allocation for streaming */ + if ((ret = as102_alloc_usb_stream_buffer(as102_dev)) != 0) + goto failed; + + /* register dvb layer */ + ret = as102_dvb_register(as102_dev); + + LEAVE(); + return ret; + +failed: + usb_set_intfdata(intf, NULL); + kfree(as102_dev); + return ret; +} + +static int as102_open(struct inode *inode, struct file *file) { + int ret = 0, minor = 0; + struct usb_interface *intf = NULL; + struct as102_dev_t *dev = NULL; + + ENTER(); + + /* read minor from inode */ + minor = iminor(inode); + + /* fetch device from usb interface */ + if((intf = usb_find_interface(&as102_usb_driver, minor)) == NULL) { + printk(KERN_ERR "%s: can't find device for minor %d\n", + __FUNCTION__, minor); + ret = -ENODEV; + goto exit; + } + + /* get our device */ + if((dev = usb_get_intfdata(intf)) == NULL) { + ret = -EFAULT; + goto exit; + } + + /* save our device object in the file's private structure */ + file->private_data = dev; + + /* increment our usage count for the device */ + kref_get(&dev->kref); + +exit: + LEAVE(); + return ret; +} + +static int as102_release(struct inode *inode, struct file *file) { + int ret = 0; + struct as102_dev_t *dev = NULL; + + ENTER(); + + if((dev = file->private_data) != NULL ) { + /* decrement the count on our device */ + kref_put(&dev->kref, as102_usb_release); + } + + LEAVE(); + return ret; +} + +MODULE_DEVICE_TABLE(usb, as102_usb_id_table); + +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as102_usb_drv.h b/drivers/staging/media/as102/as102_usb_drv.h new file mode 100644 index 00000000000..abb858ececb --- /dev/null +++ b/drivers/staging/media/as102/as102_usb_drv.h @@ -0,0 +1,54 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet + * + * 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 + +#ifndef _AS102_USB_DRV_H_ +#define _AS102_USB_DRV_H_ + +#define AS102_USB_DEVICE_TX_CTRL_CMD 0xF1 +#define AS102_USB_DEVICE_RX_CTRL_CMD 0xF2 + +/* define these values to match the supported devices */ + +/* Abilis system: "TITAN" */ +#define AS102_USB_DEVICE_VENDOR_ID 0x1BA6 +#define AS102_USB_DEVICE_PID_0001 0x0001 + +/* PCTV Systems: PCTV picoStick (74e) */ +#define DEVICE_FULL_NAME "PCTV Systems : PCTV picoStick (74e)" +#define PCTV_74E_USB_VID 0x2013 +#define PCTV_74E_USB_PID 0x0246 + +extern struct file_operations as102_dev_fops; + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)) +void as102_urb_stream_irq(struct urb *urb, struct pt_regs *regs); +#else +void as102_urb_stream_irq(struct urb *urb); +#endif + + +struct as10x_usb_token_cmd_t { + /* token cmd */ + struct as10x_cmd_t c; + /* token response */ + struct as10x_cmd_t r; +}; +#endif +/* EOF - vim: set textwidth=80 ts=8 sw=8 sts=8 noet: */ diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c new file mode 100644 index 00000000000..cecb809a51a --- /dev/null +++ b/drivers/staging/media/as102/as10x_cmd.c @@ -0,0 +1,478 @@ +/* + * Abilis Systems Single DVB-T Receiver + * Copyright (C) 2008 Pierrick Hascoet + * + * 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(LINUX) && defined(__KERNEL__) /* linux kernel implementation */ +#include +#include "as102_drv.h" +#elif defined(WIN32) + #if defined(__BUILDMACHINE__) && (__BUILDMACHINE__ == WinDDK) /* win32 ddk implementation */ + #include "wdm.h" + #include "Device.h" + #include "endian_mgmt.h" /* FIXME */ + #else /* win32 sdk implementation */ + #include + #include "types.h" + #include "util.h" + #include "as10x_handle.h" + #include "endian_mgmt.h" + #endif +#else /* all other cases */ + #include + #include "types.h" + #include "util.h" + #include "as10x_handle.h" + #include "endian_mgmt.h" /* FIXME */ +#endif /* __KERNEL__ */ + +#include "as10x_types.h" +#include "as10x_cmd.h" + +/** + \brief send turn on command to AS10x + \param phandle: pointer to AS10x handle + \return 0 when no error, < 0 in case of error. + \callgraph +*/ +int as10x_cmd_turn_on(as10x_handle_t *phandle) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + ENTER(); + + pcmd = phandle->cmd; + prsp = phandle->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd,(++phandle->cmd_xid), sizeof(pcmd->body.turn_on.req)); + + /* fill command */ + pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON); + + /* send command */ + if(phandle->ops->xfer_cmd) { + error = phandle->ops->xfer_cmd( + phandle, + (uint8_t *) pcmd, + sizeof(pcmd->body.turn_on.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.turn_on.rsp) + HEADER_SIZE); + } + else{ + error = AS10X_CMD_ERROR; + } + + if(error < 0) { + goto out; + } + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP); + +out: + LEAVE(); + return(error); +} + +/** + \brief send turn off command to AS10x + \param phandle: pointer to AS10x handle + \return 0 when no error, < 0 in case of error. + \callgraph +*/ +int as10x_cmd_turn_off(as10x_handle_t *phandle) +{ + int error; + struct as10x_cmd_t *pcmd, *prsp; + + ENTER(); + + pcmd = phandle->cmd; + prsp = phandle->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd,(++phandle->cmd_xid),sizeof(pcmd->body.turn_off.req)); + + /* fill command */ + pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF); + + /* send command */ + if(phandle->ops->xfer_cmd) { + error = phandle->ops->xfer_cmd( + phandle, (uint8_t *) pcmd, + sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); + } + else{ + error = AS10X_CMD_ERROR; + } + + if(error < 0) { + goto out; + } + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP); + +out: + LEAVE(); + return(error); +} + +/** + \brief send set tune command to AS10x + \param phandle: pointer to AS10x handle + \param ptune: tune parameters + \return 0 when no error, < 0 in case of error. + \callgraph + */ +int as10x_cmd_set_tune(as10x_handle_t *phandle, struct as10x_tune_args *ptune) +{ + int error; + struct as10x_cmd_t *preq, *prsp; + + ENTER(); + + preq = phandle->cmd; + prsp = phandle->rsp; + + /* prepare command */ + as10x_cmd_build(preq,(++phandle->cmd_xid),sizeof(preq->body.set_tune.req)); + + /* fill command */ + preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE); + preq->body.set_tune.req.args.freq = cpu_to_le32(ptune->freq); + preq->body.set_tune.req.args.bandwidth = ptune->bandwidth; + preq->body.set_tune.req.args.hier_select = ptune->hier_select; + preq->body.set_tune.req.args.constellation = ptune->constellation; + preq->body.set_tune.req.args.hierarchy = ptune->hierarchy; + preq->body.set_tune.req.args.interleaving_mode = ptune->interleaving_mode; + preq->body.set_tune.req.args.code_rate = ptune->code_rate; + preq->body.set_tune.req.args.guard_interval = ptune->guard_interval; + preq->body.set_tune.req.args.transmission_mode = ptune->transmission_mode; + + /* send command */ + if(phandle->ops->xfer_cmd) { + error = phandle->ops->xfer_cmd(phandle, + (uint8_t *) preq, + sizeof(preq->body.set_tune.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.set_tune.rsp) + HEADER_SIZE); + } else{ + error = AS10X_CMD_ERROR; + } + + if(error < 0) { + goto out; + } + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP); + +out: + LEAVE(); + return(error); +} + +/** + \brief send get tune status command to AS10x + \param phandle: pointer to AS10x handle + \param pstatus: pointer to updated status structure of the current tune + \return 0 when no error, < 0 in case of error. + \callgraph + */ +int as10x_cmd_get_tune_status(as10x_handle_t *phandle, struct as10x_tune_status *pstatus) +{ + int error; + struct as10x_cmd_t *preq, *prsp; + + ENTER(); + + preq = phandle->cmd; + prsp = phandle->rsp; + + /* prepare command */ + as10x_cmd_build(preq,(++phandle->cmd_xid), + sizeof(preq->body.get_tune_status.req)); + + /* fill command */ + preq->body.get_tune_status.req.proc_id = + cpu_to_le16(CONTROL_PROC_GETTUNESTAT); + + /* send command */ + if (phandle->ops->xfer_cmd) { + error = phandle->ops->xfer_cmd( + phandle, + (uint8_t *) preq, + sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, + (uint8_t *) prsp, + sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); + } + else{ + error = AS10X_CMD_ERROR; + } + + if (error < 0) { + goto out; + } + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP); + if (error < 0) { + goto out; + } + + /* Response OK -> get response data */ + pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state; + pstatus->signal_strength = + le16_to_cpu(prsp->body.get_tune_status.rsp.sts.signal_strength); + pstatus->PER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.PER); + pstatus->BER = le16_to_cpu(prsp->body.get_tune_status.rsp.sts.BER); + +out: + LEAVE(); + return(error); +} + +/** + \brief send get TPS command to AS10x + \param phandle: pointer to AS10x handle + \param ptps: pointer to TPS parameters structure + \return 0 when no error, < 0 in case of error. + \callgraph + */ +int as10x_cmd_get_tps(as10x_handle_t *phandle, struct as10x_tps *ptps) +{ + + int error; + struct as10x_cmd_t *pcmd, *prsp; + + ENTER(); + + pcmd = phandle->cmd; + prsp = phandle->rsp; + + /* prepare command */ + as10x_cmd_build(pcmd, (++phandle->cmd_xid),sizeof(pcmd->body.get_tps.req)); + + /* fill command */ + pcmd->body.get_tune_status.req.proc_id = cpu_to_le16(CONTROL_PROC_GETTPS); + + /* send command */ + if(phandle->ops->xfer_cmd) { + error = phandle->ops->xfer_cmd(phandle, + (uint8_t *) pcmd, sizeof(pcmd->body.get_tps.req) + HEADER_SIZE, + (uint8_t *) prsp, sizeof(prsp->body.get_tps.rsp) + HEADER_SIZE); + } + else{ + error = AS10X_CMD_ERROR; + } + + if(error < 0) { + goto out; + } + + /* parse response */ + error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP); + if (error < 0) { + goto out; + } + + /* Response OK -> get response data */ + ptps->constellation = prsp->body.get_tps.rsp.tps.constellation; + ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy; + ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode; + ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP; + ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP; + ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval; + ptps->transmission_mode = prsp->body.get_tps.rsp.tps.transmission_mode; + ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP; + ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP; + ptps->cell_ID = le16_to_cpu(prsp->body.get_tps.rsp.tps.cell_ID); + +out: + LEAVE(); + return(error); +} + +/** + \brief send get demod stats command to AS10x + \param phandle: pointer to AS10x handle + \param pdemod_stats: pointer to demod stats parameters structure + \return 0 when no erro