diff options
author | Martin Wache <M.Wache@gmx.net> | 2016-12-13 21:21:41 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-02-03 13:54:20 +0100 |
commit | 5332b6263742cd75963eccdea9e3af4a464c2999 (patch) | |
tree | 90b31c76e4dbccd2adedcbb727c3a8acc50ec11f /drivers/media | |
parent | [media] drivers: media: i2c: ml86v7667: constify v4l2_subdev_* structures (diff) | |
download | linux-5332b6263742cd75963eccdea9e3af4a464c2999.tar.xz linux-5332b6263742cd75963eccdea9e3af4a464c2999.zip |
[media] dib7000p: avoid division by zero
dib7000p_read_word() may return zero on i2c errors, resulting in
dib7000p_get_internal_freq() returning zero.
So don't divide by the result of dib7000p_get_internal_freq()
without checking it for zero in dib7000p_set_dds().
On one of my machines the device
ID 2304:0229 Pinnacle Systems, Inc. PCTV Dual DVB-T 2001e
about once a day/every two days gets into a state, where
most (all?) I2C reads return with an error. Tuning during this
state will result in a divide by zero without this patch.
This patch doesn't fix the root cause for the device getting
into a bad state, but it allows me to unload/reload the drivers,
bringing it back into a usable state.
Signed-off-by: Martin Wache <M.Wache@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/dvb-frontends/dib7000p.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index a27c0001f2d6..3815ea515364 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -805,13 +805,19 @@ static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) return 0; } -static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) +static int dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) { u32 internal = dib7000p_get_internal_freq(state); - s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */ + s32 unit_khz_dds_val; u32 abs_offset_khz = ABS(offset_khz); u32 dds = state->cfg.bw->ifreq & 0x1ffffff; u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); + if (internal == 0) { + pr_warn("DIB7000P: dib7000p_get_internal_freq returned 0\n"); + return -1; + } + /* 2**26 / Fsampling is the unit 1KHz offset */ + unit_khz_dds_val = 67108864 / (internal); dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d\n", offset_khz, internal, invert); @@ -828,6 +834,7 @@ static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9))); dib7000p_write_word(state, 22, (u16) (dds & 0xffff)); } + return 0; } static int dib7000p_agc_startup(struct dvb_frontend *demod) @@ -867,7 +874,9 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod) frequency_offset = (s32)frequency_tuner / 1000 - ch->frequency / 1000; } - dib7000p_set_dds(state, frequency_offset); + if (dib7000p_set_dds(state, frequency_offset) < 0) + return -1; + ret = 7; (*agc_state)++; break; |