From fb493d2f08b44ff2b98cfe78e3a29d4ffb6e800c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 6 Sep 2008 09:52:30 -0400 Subject: xc5000: kill xc5000_priv.h From: Michael Krufky move struct xc5000_priv into xc5000.c and delete xc5000_priv.h Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/common/tuners/xc5000.c | 13 ++++++++- linux/drivers/media/common/tuners/xc5000_priv.h | 37 ------------------------- 2 files changed, 12 insertions(+), 38 deletions(-) delete mode 100644 linux/drivers/media/common/tuners/xc5000_priv.h (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index 2c0a5b8d5..92b3ca786 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -30,7 +30,6 @@ #include "dvb_frontend.h" #include "xc5000.h" -#include "xc5000_priv.h" static int debug; module_param(debug, int, 0644); @@ -46,6 +45,18 @@ MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); #define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw" #define XC5000_DEFAULT_FIRMWARE_SIZE 12332 +struct xc5000_priv { + struct xc5000_config *cfg; + struct i2c_adapter *i2c; + + u32 freq_hz; + u32 bandwidth; + u8 video_standard; + u8 rf_mode; + + void *devptr; +}; + /* Misc Defines */ #define MAX_TV_STANDARD 23 #define XC_MAX_I2C_WRITE_LENGTH 64 diff --git a/linux/drivers/media/common/tuners/xc5000_priv.h b/linux/drivers/media/common/tuners/xc5000_priv.h deleted file mode 100644 index b2a0074c9..000000000 --- a/linux/drivers/media/common/tuners/xc5000_priv.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" - * - * Copyright (c) 2007 Steven Toth - * - * 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 of the License, 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. - */ - -#ifndef XC5000_PRIV_H -#define XC5000_PRIV_H - -struct xc5000_priv { - struct xc5000_config *cfg; - struct i2c_adapter *i2c; - - u32 freq_hz; - u32 bandwidth; - u8 video_standard; - u8 rf_mode; - - void *devptr; -}; - -#endif -- cgit v1.2.3 From dbd3f1f18f6dc2b7e14b1a4576af6f5f8376ad41 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 6 Sep 2008 10:44:53 -0400 Subject: xc5000: allow multiple driver instances for the same hardware to share state From: Michael Krufky Convert xc5000 to use the hybrid_tuner_request_state and hybrid_tuner_release_state macros to manage state sharing between hybrid tuner instances. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/common/tuners/xc5000.c | 87 +++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 26 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index 92b3ca786..54049415c 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -30,6 +30,7 @@ #include "dvb_frontend.h" #include "xc5000.h" +#include "tuner-i2c.h" static int debug; module_param(debug, int, 0644); @@ -39,6 +40,9 @@ static int xc5000_load_fw_on_attach; module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644); MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); +static DEFINE_MUTEX(xc5000_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg) @@ -47,7 +51,9 @@ MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); struct xc5000_priv { struct xc5000_config *cfg; - struct i2c_adapter *i2c; + + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; u32 freq_hz; u32 bandwidth; @@ -539,13 +545,13 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) u8 buf[2] = { reg >> 8, reg & 0xff }; u8 bval[2] = { 0, 0 }; struct i2c_msg msg[2] = { - { .addr = priv->cfg->i2c_address, + { .addr = priv->i2c_props.addr, .flags = 0, .buf = &buf[0], .len = 2 }, - { .addr = priv->cfg->i2c_address, + { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, }; - if (i2c_transfer(priv->i2c, msg, 2) != 2) { + if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { printk(KERN_WARNING "xc5000: I2C read failed\n"); return -EREMOTEIO; } @@ -556,10 +562,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len) { - struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0, .buf = buf, .len = len }; - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", (int)len); return -EREMOTEIO; @@ -569,10 +575,10 @@ static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len) static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len) { - struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, .buf = buf, .len = len }; - if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len); return -EREMOTEIO; } @@ -589,7 +595,7 @@ static int xc5000_fwupload(struct dvb_frontend* fe) printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", XC5000_DEFAULT_FIRMWARE); - ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev); + ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c_props.adap->dev); if (ret) { printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); ret = XC_RESULT_RESET_FAILURE; @@ -927,9 +933,19 @@ static int xc5000_init(struct dvb_frontend *fe) static int xc5000_release(struct dvb_frontend *fe) { + struct xc5000_priv *priv = fe->tuner_priv; + dprintk(1, "%s()\n", __func__); - kfree(fe->tuner_priv); + + mutex_lock(&xc5000_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&xc5000_list_mutex); + fe->tuner_priv = NULL; + return 0; } @@ -957,26 +973,41 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, struct xc5000_config *cfg, void *devptr) { struct xc5000_priv *priv = NULL; + int instance; u16 id = 0; - dprintk(1, "%s()\n", __func__); + dprintk(1, "%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); - priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + mutex_lock(&xc5000_list_mutex); - priv->cfg = cfg; - priv->bandwidth = BANDWIDTH_6_MHZ; - priv->i2c = i2c; - priv->devptr = devptr; + instance = hybrid_tuner_request_state(struct xc5000_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_address, "xc5000"); + switch (instance) { + case 0: + goto fail; + break; + case 1: + /* new tuner instance */ + priv->cfg = cfg; + priv->bandwidth = BANDWIDTH_6_MHZ; + priv->devptr = devptr; + + fe->tuner_priv = priv; + break; + default: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ - if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) { - kfree(priv); - return NULL; - } + if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) + goto fail; switch(id) { case XC_PRODUCT_ID_FW_LOADED: @@ -997,19 +1028,23 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, printk(KERN_ERR "xc5000: Device not found at addr 0x%02x (0x%x)\n", cfg->i2c_address, id); - kfree(priv); - return NULL; + goto fail; } + mutex_unlock(&xc5000_list_mutex); + memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = priv; - if (xc5000_load_fw_on_attach) xc5000_init(fe); return fe; +fail: + mutex_unlock(&xc5000_list_mutex); + + xc5000_release(fe); + return NULL; } EXPORT_SYMBOL(xc5000_attach); -- cgit v1.2.3 From 8cab49c4acd229cb28d9add11a80eed55859ba2d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 6 Sep 2008 12:54:45 -0400 Subject: xc5000: prevent an OOPS if analog driver is unloaded while digital is in use From: Michael Krufky Prevent an OOPS if xc5000_attach was called by tuner.ko before being called by the DVB adapter driver. The OOPS occurs when a digital tune request is made after tuner.ko is unloaded. When tuner.ko is unloaded, it takes the xc5000_config structure with it. Rather than storing a pointer to the xc5000_config structure, just store the if_khz and tuner_callback inside the xc5000_priv internal state structure. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/common/tuners/xc5000.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index 54049415c..134366525 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -50,17 +50,17 @@ static LIST_HEAD(hybrid_tuner_instance_list); #define XC5000_DEFAULT_FIRMWARE_SIZE 12332 struct xc5000_priv { - struct xc5000_config *cfg; - struct tuner_i2c_props i2c_props; struct list_head hybrid_tuner_instance_list; + u32 if_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; u8 rf_mode; void *devptr; + int (*tuner_callback) (void *priv, int command, int arg); }; /* Misc Defines */ @@ -233,9 +233,8 @@ static void xc5000_TunerReset(struct dvb_frontend *fe) dprintk(1, "%s()\n", __func__); - if (priv->cfg->tuner_callback) { - ret = priv->cfg->tuner_callback(priv->devptr, - XC5000_TUNER_RESET, 0); + if (priv->tuner_callback) { + ret = priv->tuner_callback(priv->devptr, XC5000_TUNER_RESET, 0); if (ret) printk(KERN_ERR "xc5000: reset failed\n"); } else @@ -711,10 +710,10 @@ static int xc5000_set_params(struct dvb_frontend *fe, return -EREMOTEIO; } - ret = xc_set_IF_frequency(priv, priv->cfg->if_khz); + ret = xc_set_IF_frequency(priv, priv->if_khz); if (ret != XC_RESULT_SUCCESS) { printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", - priv->cfg->if_khz); + priv->if_khz); return -EIO; } @@ -991,9 +990,10 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, break; case 1: /* new tuner instance */ - priv->cfg = cfg; priv->bandwidth = BANDWIDTH_6_MHZ; priv->devptr = devptr; + priv->if_khz = cfg->if_khz; + priv->tuner_callback = cfg->tuner_callback; fe->tuner_priv = priv; break; -- cgit v1.2.3 From 507449b0440221c834914389d808f4728dcd2702 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 6 Sep 2008 13:56:58 -0400 Subject: xc5000: dont pass devptr in xc5000_attach() From: Michael Krufky Dont pass devptr in xc5000_attach, dont store it in xc5000_priv. This pointer is passed into the tuner_callback function, which always expects a pointer to fe->dvb->priv or i2c_adapter->algo_data. This prevents future possible bugs in new drivers, such as using a "devptr" other that the standard fe->dvb->priv in a DVB driver. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/common/tuners/xc5000.c | 9 +++++---- linux/drivers/media/common/tuners/xc5000.h | 6 ++---- linux/drivers/media/video/au0828/au0828-dvb.c | 5 ++--- linux/drivers/media/video/cx23885/cx23885-dvb.c | 8 ++++---- linux/drivers/media/video/cx88/cx88-dvb.c | 12 ++---------- linux/drivers/media/video/tuner-core.c | 3 +-- 6 files changed, 16 insertions(+), 27 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index 134366525..7d4d82d23 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -59,7 +59,6 @@ struct xc5000_priv { u8 video_standard; u8 rf_mode; - void *devptr; int (*tuner_callback) (void *priv, int command, int arg); }; @@ -234,7 +233,10 @@ static void xc5000_TunerReset(struct dvb_frontend *fe) dprintk(1, "%s()\n", __func__); if (priv->tuner_callback) { - ret = priv->tuner_callback(priv->devptr, XC5000_TUNER_RESET, 0); + ret = priv->tuner_callback(((fe->dvb) && (fe->dvb->priv)) ? + fe->dvb->priv : + priv->i2c_props.adap->algo_data, + XC5000_TUNER_RESET, 0); if (ret) printk(KERN_ERR "xc5000: reset failed\n"); } else @@ -969,7 +971,7 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = { struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, void *devptr) + struct xc5000_config *cfg) { struct xc5000_priv *priv = NULL; int instance; @@ -991,7 +993,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, case 1: /* new tuner instance */ priv->bandwidth = BANDWIDTH_6_MHZ; - priv->devptr = devptr; priv->if_khz = cfg->if_khz; priv->tuner_callback = cfg->tuner_callback; diff --git a/linux/drivers/media/common/tuners/xc5000.h b/linux/drivers/media/common/tuners/xc5000.h index 5389f7409..fa0321cfd 100644 --- a/linux/drivers/media/common/tuners/xc5000.h +++ b/linux/drivers/media/common/tuners/xc5000.h @@ -49,13 +49,11 @@ struct xc5000_config { (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE)) extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, - void *devptr); + struct xc5000_config *cfg); #else static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - struct xc5000_config *cfg, - void *devptr) + struct xc5000_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c index ef088bc12..24160ca59 100644 --- a/linux/drivers/media/video/au0828/au0828-dvb.c +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -363,9 +363,8 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig, dev); + dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig); break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 0d8ce96cf..663954988 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -391,8 +391,8 @@ static int dvb_register(struct cx23885_tsport *port) &dev->i2c_bus[0].i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_hvr1500q_tunerconfig, port); + &i2c_bus->i2c_adap, + &hauppauge_hvr1500q_tunerconfig); break; case CX23885_BOARD_HAUPPAUGE_HVR1500: i2c_bus = &dev->i2c_bus[1]; @@ -472,8 +472,8 @@ static int dvb_register(struct cx23885_tsport *port) &i2c_bus->i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &dvico_xc5000_tunerconfig, port); + &i2c_bus->i2c_adap, + &dvico_xc5000_tunerconfig); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: { i2c_bus = &dev->i2c_bus[port->nr - 1]; diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index fc5807aeb..68e56be1f 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -820,13 +820,9 @@ static int dvb_register(struct cx8802_dev *dev) &pinnacle_pctv_hd_800i_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &pinnacle_pctv_hd_800i_tuner_config, - core->i2c_adap.algo_data)) + &pinnacle_pctv_hd_800i_tuner_config)) goto frontend_detach; } break; @@ -881,13 +877,9 @@ static int dvb_register(struct cx8802_dev *dev) &dvico_fusionhdtv7_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &dvico_fusionhdtv7_tuner_config, - core->i2c_adap.algo_data)) + &dvico_fusionhdtv7_tuner_config)) goto frontend_detach; } break; diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 9649281a9..d6ce6f6a5 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -453,8 +453,7 @@ static void set_type(struct i2c_client *c, unsigned int type, xc5000_cfg.if_khz = 5380; xc5000_cfg.tuner_callback = t->tuner_callback; if (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000_cfg, - c->adapter->algo_data)) + &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; xc_tuner_ops = &t->fe.ops.tuner_ops; -- cgit v1.2.3