OpenCloudOS-Kernel/drivers/media/dvb-frontends/au8522_common.c

267 lines
6.8 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
Auvitek AU8522 QAM/8VSB demodulator driver
Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
Copyright (C) 2005-2008 Auvitek International, Ltd.
Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
*/
#include <linux/i2c.h>
#include <media/dvb_frontend.h>
#include "au8522_priv.h"
static int debug;
#define dprintk(arg...)\
do { if (debug)\
printk(arg);\
} while (0)
/* Despite the name "hybrid_tuner", the framework works just as well for
hybrid demodulators as well... */
static LIST_HEAD(hybrid_tuner_instance_list);
static DEFINE_MUTEX(au8522_list_mutex);
/* 16 bit registers, 8 bit values */
int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
{
int ret;
u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
[media] au8522: Avoid memory leak for device config data As reported by kmemleak: unreferenced object 0xffff880321e1da40 (size 32): comm "modprobe", pid 3309, jiffies 4295019569 (age 2359.636s) hex dump (first 32 bytes): 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 G............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<ffffffff82278c8e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8153c08c>] kmem_cache_alloc_trace+0x1ec/0x280 [<ffffffffa13a896a>] au8522_probe+0x19a/0xa30 [au8522_decoder] [<ffffffff81de0032>] i2c_device_probe+0x2b2/0x490 [<ffffffff81ca7004>] driver_probe_device+0x454/0xd90 [<ffffffff81ca7c1b>] __device_attach_driver+0x17b/0x230 [<ffffffff81ca15da>] bus_for_each_drv+0x11a/0x1b0 [<ffffffff81ca6a4d>] __device_attach+0x1cd/0x2c0 [<ffffffff81ca7d43>] device_initial_probe+0x13/0x20 [<ffffffff81ca451f>] bus_probe_device+0x1af/0x250 [<ffffffff81c9e0f3>] device_add+0x943/0x13b0 [<ffffffff81c9eb7a>] device_register+0x1a/0x20 [<ffffffff81de8626>] i2c_new_device+0x5d6/0x8f0 [<ffffffffa0d88ea4>] v4l2_i2c_new_subdev_board+0x1e4/0x250 [v4l2_common] [<ffffffffa0d88fe7>] v4l2_i2c_new_subdev+0xd7/0x110 [v4l2_common] [<ffffffffa13b2f76>] au0828_card_analog_fe_setup+0x2e6/0x3f0 [au0828] Checking where the error happens: (gdb) list *au8522_probe+0x19a 0x99a is in au8522_probe (drivers/media/dvb-frontends/au8522_decoder.c:761). 756 printk(KERN_INFO "au8522_decoder attach existing instance.\n"); 757 break; 758 } 759 760 demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); 761 if (demod_config == NULL) { 762 if (instance == 1) 763 kfree(state); 764 return -ENOMEM; 765 } Shows that the error path is not being handled properly. The are actually several issues here: 1) config free should have been calling hybrid_tuner_release_state() function, by calling au8522_release_state(); 2) config is only allocated at the digital part. On the analog one, it is received from the caller. A complex logic could be added to address it, however, it is simpler to just embeed config inside the state. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-12-22 00:38:31 +08:00
struct i2c_msg msg = { .addr = state->config.demod_address,
.flags = 0, .buf = buf, .len = 3 };
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
__func__, reg, data, ret);
return (ret != 1) ? -1 : 0;
}
EXPORT_SYMBOL(au8522_writereg);
u8 au8522_readreg(struct au8522_state *state, u16 reg)
{
int ret;
u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
[media] au8522: Avoid memory leak for device config data As reported by kmemleak: unreferenced object 0xffff880321e1da40 (size 32): comm "modprobe", pid 3309, jiffies 4295019569 (age 2359.636s) hex dump (first 32 bytes): 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 G............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<ffffffff82278c8e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8153c08c>] kmem_cache_alloc_trace+0x1ec/0x280 [<ffffffffa13a896a>] au8522_probe+0x19a/0xa30 [au8522_decoder] [<ffffffff81de0032>] i2c_device_probe+0x2b2/0x490 [<ffffffff81ca7004>] driver_probe_device+0x454/0xd90 [<ffffffff81ca7c1b>] __device_attach_driver+0x17b/0x230 [<ffffffff81ca15da>] bus_for_each_drv+0x11a/0x1b0 [<ffffffff81ca6a4d>] __device_attach+0x1cd/0x2c0 [<ffffffff81ca7d43>] device_initial_probe+0x13/0x20 [<ffffffff81ca451f>] bus_probe_device+0x1af/0x250 [<ffffffff81c9e0f3>] device_add+0x943/0x13b0 [<ffffffff81c9eb7a>] device_register+0x1a/0x20 [<ffffffff81de8626>] i2c_new_device+0x5d6/0x8f0 [<ffffffffa0d88ea4>] v4l2_i2c_new_subdev_board+0x1e4/0x250 [v4l2_common] [<ffffffffa0d88fe7>] v4l2_i2c_new_subdev+0xd7/0x110 [v4l2_common] [<ffffffffa13b2f76>] au0828_card_analog_fe_setup+0x2e6/0x3f0 [au0828] Checking where the error happens: (gdb) list *au8522_probe+0x19a 0x99a is in au8522_probe (drivers/media/dvb-frontends/au8522_decoder.c:761). 756 printk(KERN_INFO "au8522_decoder attach existing instance.\n"); 757 break; 758 } 759 760 demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); 761 if (demod_config == NULL) { 762 if (instance == 1) 763 kfree(state); 764 return -ENOMEM; 765 } Shows that the error path is not being handled properly. The are actually several issues here: 1) config free should have been calling hybrid_tuner_release_state() function, by calling au8522_release_state(); 2) config is only allocated at the digital part. On the analog one, it is received from the caller. A complex logic could be added to address it, however, it is simpler to just embeed config inside the state. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-12-22 00:38:31 +08:00
{ .addr = state->config.demod_address, .flags = 0,
.buf = b0, .len = 2 },
[media] au8522: Avoid memory leak for device config data As reported by kmemleak: unreferenced object 0xffff880321e1da40 (size 32): comm "modprobe", pid 3309, jiffies 4295019569 (age 2359.636s) hex dump (first 32 bytes): 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 G............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<ffffffff82278c8e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8153c08c>] kmem_cache_alloc_trace+0x1ec/0x280 [<ffffffffa13a896a>] au8522_probe+0x19a/0xa30 [au8522_decoder] [<ffffffff81de0032>] i2c_device_probe+0x2b2/0x490 [<ffffffff81ca7004>] driver_probe_device+0x454/0xd90 [<ffffffff81ca7c1b>] __device_attach_driver+0x17b/0x230 [<ffffffff81ca15da>] bus_for_each_drv+0x11a/0x1b0 [<ffffffff81ca6a4d>] __device_attach+0x1cd/0x2c0 [<ffffffff81ca7d43>] device_initial_probe+0x13/0x20 [<ffffffff81ca451f>] bus_probe_device+0x1af/0x250 [<ffffffff81c9e0f3>] device_add+0x943/0x13b0 [<ffffffff81c9eb7a>] device_register+0x1a/0x20 [<ffffffff81de8626>] i2c_new_device+0x5d6/0x8f0 [<ffffffffa0d88ea4>] v4l2_i2c_new_subdev_board+0x1e4/0x250 [v4l2_common] [<ffffffffa0d88fe7>] v4l2_i2c_new_subdev+0xd7/0x110 [v4l2_common] [<ffffffffa13b2f76>] au0828_card_analog_fe_setup+0x2e6/0x3f0 [au0828] Checking where the error happens: (gdb) list *au8522_probe+0x19a 0x99a is in au8522_probe (drivers/media/dvb-frontends/au8522_decoder.c:761). 756 printk(KERN_INFO "au8522_decoder attach existing instance.\n"); 757 break; 758 } 759 760 demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); 761 if (demod_config == NULL) { 762 if (instance == 1) 763 kfree(state); 764 return -ENOMEM; 765 } Shows that the error path is not being handled properly. The are actually several issues here: 1) config free should have been calling hybrid_tuner_release_state() function, by calling au8522_release_state(); 2) config is only allocated at the digital part. On the analog one, it is received from the caller. A complex logic could be added to address it, however, it is simpler to just embeed config inside the state. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-12-22 00:38:31 +08:00
{ .addr = state->config.demod_address, .flags = I2C_M_RD,
.buf = b1, .len = 1 } };
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2)
printk(KERN_ERR "%s: readreg error (ret == %i)\n",
__func__, ret);
return b1[0];
}
EXPORT_SYMBOL(au8522_readreg);
int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct au8522_state *state = fe->demodulator_priv;
dprintk("%s(%d)\n", __func__, enable);
if (state->operational_mode == AU8522_ANALOG_MODE) {
/* We're being asked to manage the gate even though we're
not in digital mode. This can occur if we get switched
over to analog mode before the dvb_frontend kernel thread
has completely shutdown */
return 0;
}
if (enable)
return au8522_writereg(state, 0x106, 1);
else
return au8522_writereg(state, 0x106, 0);
}
EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct au8522_state *state = fe->demodulator_priv;
dprintk("%s(%d)\n", __func__, enable);
if (enable)
return au8522_writereg(state, 0x106, 1);
else
return au8522_writereg(state, 0x106, 0);
}
EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
/* Reset the demod hardware and reset all of the configuration registers
to a default state. */
int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
u8 client_address)
{
int ret;
mutex_lock(&au8522_list_mutex);
ret = hybrid_tuner_request_state(struct au8522_state, (*state),
hybrid_tuner_instance_list,
i2c, client_address, "au8522");
mutex_unlock(&au8522_list_mutex);
return ret;
}
EXPORT_SYMBOL(au8522_get_state);
void au8522_release_state(struct au8522_state *state)
{
mutex_lock(&au8522_list_mutex);
if (state != NULL)
hybrid_tuner_release_state(state);
mutex_unlock(&au8522_list_mutex);
}
EXPORT_SYMBOL(au8522_release_state);
static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
{
[media] au8522: Avoid memory leak for device config data As reported by kmemleak: unreferenced object 0xffff880321e1da40 (size 32): comm "modprobe", pid 3309, jiffies 4295019569 (age 2359.636s) hex dump (first 32 bytes): 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 G............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<ffffffff82278c8e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8153c08c>] kmem_cache_alloc_trace+0x1ec/0x280 [<ffffffffa13a896a>] au8522_probe+0x19a/0xa30 [au8522_decoder] [<ffffffff81de0032>] i2c_device_probe+0x2b2/0x490 [<ffffffff81ca7004>] driver_probe_device+0x454/0xd90 [<ffffffff81ca7c1b>] __device_attach_driver+0x17b/0x230 [<ffffffff81ca15da>] bus_for_each_drv+0x11a/0x1b0 [<ffffffff81ca6a4d>] __device_attach+0x1cd/0x2c0 [<ffffffff81ca7d43>] device_initial_probe+0x13/0x20 [<ffffffff81ca451f>] bus_probe_device+0x1af/0x250 [<ffffffff81c9e0f3>] device_add+0x943/0x13b0 [<ffffffff81c9eb7a>] device_register+0x1a/0x20 [<ffffffff81de8626>] i2c_new_device+0x5d6/0x8f0 [<ffffffffa0d88ea4>] v4l2_i2c_new_subdev_board+0x1e4/0x250 [v4l2_common] [<ffffffffa0d88fe7>] v4l2_i2c_new_subdev+0xd7/0x110 [v4l2_common] [<ffffffffa13b2f76>] au0828_card_analog_fe_setup+0x2e6/0x3f0 [au0828] Checking where the error happens: (gdb) list *au8522_probe+0x19a 0x99a is in au8522_probe (drivers/media/dvb-frontends/au8522_decoder.c:761). 756 printk(KERN_INFO "au8522_decoder attach existing instance.\n"); 757 break; 758 } 759 760 demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); 761 if (demod_config == NULL) { 762 if (instance == 1) 763 kfree(state); 764 return -ENOMEM; 765 } Shows that the error path is not being handled properly. The are actually several issues here: 1) config free should have been calling hybrid_tuner_release_state() function, by calling au8522_release_state(); 2) config is only allocated at the digital part. On the analog one, it is received from the caller. A complex logic could be added to address it, however, it is simpler to just embeed config inside the state. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-12-22 00:38:31 +08:00
struct au8522_led_config *led_config = state->config.led_cfg;
u8 val;
/* bail out if we can't control an LED */
if (!led_config || !led_config->gpio_output ||
!led_config->gpio_output_enable || !led_config->gpio_output_disable)
return 0;
val = au8522_readreg(state, 0x4000 |
(led_config->gpio_output & ~0xc000));
if (onoff) {
/* enable GPIO output */
val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
val |= (led_config->gpio_output_enable & 0xff);
} else {
/* disable GPIO output */
val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
val |= (led_config->gpio_output_disable & 0xff);
}
return au8522_writereg(state, 0x8000 |
(led_config->gpio_output & ~0xc000), val);
}
/* led = 0 | off
* led = 1 | signal ok
* led = 2 | signal strong
* led < 0 | only light led if leds are currently off
*/
int au8522_led_ctrl(struct au8522_state *state, int led)
{
[media] au8522: Avoid memory leak for device config data As reported by kmemleak: unreferenced object 0xffff880321e1da40 (size 32): comm "modprobe", pid 3309, jiffies 4295019569 (age 2359.636s) hex dump (first 32 bytes): 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 G............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<ffffffff82278c8e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8153c08c>] kmem_cache_alloc_trace+0x1ec/0x280 [<ffffffffa13a896a>] au8522_probe+0x19a/0xa30 [au8522_decoder] [<ffffffff81de0032>] i2c_device_probe+0x2b2/0x490 [<ffffffff81ca7004>] driver_probe_device+0x454/0xd90 [<ffffffff81ca7c1b>] __device_attach_driver+0x17b/0x230 [<ffffffff81ca15da>] bus_for_each_drv+0x11a/0x1b0 [<ffffffff81ca6a4d>] __device_attach+0x1cd/0x2c0 [<ffffffff81ca7d43>] device_initial_probe+0x13/0x20 [<ffffffff81ca451f>] bus_probe_device+0x1af/0x250 [<ffffffff81c9e0f3>] device_add+0x943/0x13b0 [<ffffffff81c9eb7a>] device_register+0x1a/0x20 [<ffffffff81de8626>] i2c_new_device+0x5d6/0x8f0 [<ffffffffa0d88ea4>] v4l2_i2c_new_subdev_board+0x1e4/0x250 [v4l2_common] [<ffffffffa0d88fe7>] v4l2_i2c_new_subdev+0xd7/0x110 [v4l2_common] [<ffffffffa13b2f76>] au0828_card_analog_fe_setup+0x2e6/0x3f0 [au0828] Checking where the error happens: (gdb) list *au8522_probe+0x19a 0x99a is in au8522_probe (drivers/media/dvb-frontends/au8522_decoder.c:761). 756 printk(KERN_INFO "au8522_decoder attach existing instance.\n"); 757 break; 758 } 759 760 demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); 761 if (demod_config == NULL) { 762 if (instance == 1) 763 kfree(state); 764 return -ENOMEM; 765 } Shows that the error path is not being handled properly. The are actually several issues here: 1) config free should have been calling hybrid_tuner_release_state() function, by calling au8522_release_state(); 2) config is only allocated at the digital part. On the analog one, it is received from the caller. A complex logic could be added to address it, however, it is simpler to just embeed config inside the state. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-12-22 00:38:31 +08:00
struct au8522_led_config *led_config = state->config.led_cfg;
int i, ret = 0;
/* bail out if we can't control an LED */
if (!led_config || !led_config->gpio_leds ||
!led_config->num_led_states || !led_config->led_states)
return 0;
if (led < 0) {
/* if LED is already lit, then leave it as-is */
if (state->led_state)
return 0;
else
led *= -1;
}
/* toggle LED if changing state */
if (state->led_state != led) {
u8 val;
dprintk("%s: %d\n", __func__, led);
au8522_led_gpio_enable(state, 1);
val = au8522_readreg(state, 0x4000 |
(led_config->gpio_leds & ~0xc000));
/* start with all leds off */
for (i = 0; i < led_config->num_led_states; i++)
val &= ~led_config->led_states[i];
/* set selected LED state */
if (led < led_config->num_led_states)
val |= led_config->led_states[led];
else if (led_config->num_led_states)
val |=
led_config->led_states[led_config->num_led_states - 1];
ret = au8522_writereg(state, 0x8000 |
(led_config->gpio_leds & ~0xc000), val);
if (ret < 0)
return ret;
state->led_state = led;
if (led == 0)
au8522_led_gpio_enable(state, 0);
}
return 0;
}
EXPORT_SYMBOL(au8522_led_ctrl);
int au8522_init(struct dvb_frontend *fe)
{
struct au8522_state *state = fe->demodulator_priv;
dprintk("%s()\n", __func__);
state->operational_mode = AU8522_DIGITAL_MODE;
/* Clear out any state associated with the digital side of the
chip, so that when it gets powered back up it won't think
that it is already tuned */
state->current_frequency = 0;
state->current_modulation = VSB_8;
au8522_writereg(state, 0xa4, 1 << 5);
au8522_i2c_gate_ctrl(fe, 1);
return 0;
}
EXPORT_SYMBOL(au8522_init);
int au8522_sleep(struct dvb_frontend *fe)
{
struct au8522_state *state = fe->demodulator_priv;
dprintk("%s()\n", __func__);
/* Only power down if the digital side is currently using the chip */
if (state->operational_mode == AU8522_ANALOG_MODE) {
/* We're not in one of the expected power modes, which means
that the DVB thread is probably telling us to go to sleep
even though the analog frontend has already started using
the chip. So ignore the request */
return 0;
}
/* turn off led */
au8522_led_ctrl(state, 0);
/* Power down the chip */
au8522_writereg(state, 0xa4, 1 << 5);
state->current_frequency = 0;
return 0;
}
EXPORT_SYMBOL(au8522_sleep);
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable verbose debug messages");
MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");