Message ID | 9ebf1fdd613b5747aefb3ee21c9c246d102f1a47.1390986082.git.moinejf@free.fr (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Sat, Jan 25, 2014 at 06:14:45PM +0100, Jean-Francois Moine wrote: > This patch adds more error checking inn I2C I/O functions. > In case of I/O error, this permits to avoid writing in bad controller > pages, a bad chipset detection or looping when getting the EDID. I've just looked at this again, and spotted something: > -static uint8_t > +static int > reg_read(struct tda998x_priv *priv, uint16_t reg) > { > uint8_t val = 0; > - reg_read_range(priv, reg, &val, sizeof(val)); > + int ret; > + > + ret = reg_read_range(priv, reg, &val, sizeof(val)); > + if (ret < 0) > + return ret; So yes, this can return negative numbers. > @@ -1158,8 +1184,11 @@ tda998x_encoder_init(struct i2c_client *client, > tda998x_reset(priv); > > /* read version: */ > - priv->rev = reg_read(priv, REG_VERSION_LSB) | > - reg_read(priv, REG_VERSION_MSB) << 8; > + ret = reg_read(priv, REG_VERSION_LSB) | > + (reg_read(priv, REG_VERSION_MSB) << 8); > + if (ret < 0) > + goto fail; > + priv->rev = ret; Two issues here: 1. The additional parens are /really/ not required. 2. What if reg_read(priv, REG_VERSION_MSB) returns a negative number? If we're going to the extent of attempting to make the read/write functions return errors, we should at least handle errors generated by them properly, otherwise it's pointless making them return errors. ret = reg_read(priv, REG_VERSION_LSB); if (ret < 0) goto fail; priv->rev = ret; ret = reg_read(priv, REG_VERSION_MSB); if (ret < 0) goto fail; priv->rev |= ret << 8; If you want it to look slightly nicer: int rev_lo, rev_hi; rev_lo = reg_read(priv, REG_VERSION_LSB); rev_hi = reg_read(priv, REG_VERSION_MSB); if (rev_lo < 0 || rev_hi < 0) { ret = rev_lo < 0 ? rev_lo : rev_hi; goto fail; } priv->rev = rev_lo | rev_hi << 8; I'm happy to commit such a change after this patch to clean it up, or if you want to regenerate your patch 2 and post /just/ that incorporating this change.
On Sun, 2 Feb 2014 16:20:58 +0000 Russell King - ARM Linux <linux@arm.linux.org.uk> wrote: > On Sat, Jan 25, 2014 at 06:14:45PM +0100, Jean-Francois Moine wrote: > > This patch adds more error checking inn I2C I/O functions. > > In case of I/O error, this permits to avoid writing in bad controller > > pages, a bad chipset detection or looping when getting the EDID. > > I've just looked at this again, and spotted something: > > > -static uint8_t > > +static int > > reg_read(struct tda998x_priv *priv, uint16_t reg) > > { > > uint8_t val = 0; > > - reg_read_range(priv, reg, &val, sizeof(val)); > > + int ret; > > + > > + ret = reg_read_range(priv, reg, &val, sizeof(val)); > > + if (ret < 0) > > + return ret; > > So yes, this can return negative numbers. > > > @@ -1158,8 +1184,11 @@ tda998x_encoder_init(struct i2c_client *client, > > tda998x_reset(priv); > > > > /* read version: */ > > - priv->rev = reg_read(priv, REG_VERSION_LSB) | > > - reg_read(priv, REG_VERSION_MSB) << 8; > > + ret = reg_read(priv, REG_VERSION_LSB) | > > + (reg_read(priv, REG_VERSION_MSB) << 8); > > + if (ret < 0) > > + goto fail; > > + priv->rev = ret; > > Two issues here: > > 1. The additional parens are /really/ not required. > 2. What if reg_read(priv, REG_VERSION_MSB) returns a negative number? > > If we're going to the extent of attempting to make the read/write > functions return errors, we should at least handle errors generated > by them properly, otherwise it's pointless making them return errors. > > ret = reg_read(priv, REG_VERSION_LSB); > if (ret < 0) > goto fail; > > priv->rev = ret; > > ret = reg_read(priv, REG_VERSION_MSB); > if (ret < 0) > goto fail; > > priv->rev |= ret << 8; > > If you want it to look slightly nicer: > > int rev_lo, rev_hi; > > rev_lo = reg_read(priv, REG_VERSION_LSB); > rev_hi = reg_read(priv, REG_VERSION_MSB); > if (rev_lo < 0 || rev_hi < 0) { > ret = rev_lo < 0 ? rev_lo : rev_hi; > goto fail; > } > > priv->rev = rev_lo | rev_hi << 8; > > I'm happy to commit such a change after this patch to clean it up, or if > you want to regenerate your patch 2 and post /just/ that incorporating > this change. I think that my code works correctly: when there is an error, the result of reg_read() is minus the error code, and this error code is always lower than 8388607 (0x7fffff). Then, reg_read() << 8 will always be negative. Otherwise, I may redo a patch about the useless parenthesis.
On Sun, Feb 02, 2014 at 06:30:00PM +0100, Jean-Francois Moine wrote: > On Sun, 2 Feb 2014 16:20:58 +0000 > Russell King - ARM Linux <linux@arm.linux.org.uk> wrote: > > > On Sat, Jan 25, 2014 at 06:14:45PM +0100, Jean-Francois Moine wrote: > > > This patch adds more error checking inn I2C I/O functions. > > > In case of I/O error, this permits to avoid writing in bad controller > > > pages, a bad chipset detection or looping when getting the EDID. > > > > I've just looked at this again, and spotted something: > > > > > -static uint8_t > > > +static int > > > reg_read(struct tda998x_priv *priv, uint16_t reg) > > > { > > > uint8_t val = 0; > > > - reg_read_range(priv, reg, &val, sizeof(val)); > > > + int ret; > > > + > > > + ret = reg_read_range(priv, reg, &val, sizeof(val)); > > > + if (ret < 0) > > > + return ret; > > > > So yes, this can return negative numbers. > > > > > @@ -1158,8 +1184,11 @@ tda998x_encoder_init(struct i2c_client *client, > > > tda998x_reset(priv); > > > > > > /* read version: */ > > > - priv->rev = reg_read(priv, REG_VERSION_LSB) | > > > - reg_read(priv, REG_VERSION_MSB) << 8; > > > + ret = reg_read(priv, REG_VERSION_LSB) | > > > + (reg_read(priv, REG_VERSION_MSB) << 8); > > > + if (ret < 0) > > > + goto fail; > > > + priv->rev = ret; > > > > Two issues here: > > > > 1. The additional parens are /really/ not required. > > 2. What if reg_read(priv, REG_VERSION_MSB) returns a negative number? > > > > If we're going to the extent of attempting to make the read/write > > functions return errors, we should at least handle errors generated > > by them properly, otherwise it's pointless making them return errors. > > > > ret = reg_read(priv, REG_VERSION_LSB); > > if (ret < 0) > > goto fail; > > > > priv->rev = ret; > > > > ret = reg_read(priv, REG_VERSION_MSB); > > if (ret < 0) > > goto fail; > > > > priv->rev |= ret << 8; > > > > If you want it to look slightly nicer: > > > > int rev_lo, rev_hi; > > > > rev_lo = reg_read(priv, REG_VERSION_LSB); > > rev_hi = reg_read(priv, REG_VERSION_MSB); > > if (rev_lo < 0 || rev_hi < 0) { > > ret = rev_lo < 0 ? rev_lo : rev_hi; > > goto fail; > > } > > > > priv->rev = rev_lo | rev_hi << 8; > > > > I'm happy to commit such a change after this patch to clean it up, or if > > you want to regenerate your patch 2 and post /just/ that incorporating > > this change. > > I think that my code works correctly: when there is an error, the > result of reg_read() is minus the error code, and this error code is > always lower than 8388607 (0x7fffff). Then, reg_read() << 8 will always > be negative. The issue I'm pointing out is not whether it will be interpreted as an error or not, it's whether the value is a correct error code. If we don't care about the reason why an error occurs, we might as well just return -1. I've added my own patch which adjusts it as per above to my tree anyway, so I'm not that worried about this.
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 26dd299..11f0607 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -362,7 +362,7 @@ fail: return 0; } -static void +static int set_page(struct tda998x_priv *priv, uint16_t reg) { if (REG2PAGE(reg) != priv->current_page) { @@ -371,11 +371,14 @@ set_page(struct tda998x_priv *priv, uint16_t reg) REG_CURPAGE, REG2PAGE(reg) }; int ret = i2c_master_send(client, buf, sizeof(buf)); - if (ret < 0) + if (ret < 0) { dev_err(&client->dev, "Error %d writing to REG_CURPAGE\n", ret); + return ret; + } priv->current_page = REG2PAGE(reg); } + return 0; } static int @@ -385,7 +388,9 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) uint8_t addr = REG2ADDR(reg); int ret; - set_page(priv, reg); + ret = set_page(priv, reg); + if (ret < 0) + return ret; ret = i2c_master_send(client, &addr, sizeof(addr)); if (ret < 0) @@ -412,18 +417,24 @@ reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt) buf[0] = REG2ADDR(reg); memcpy(&buf[1], p, cnt); - set_page(priv, reg); + ret = set_page(priv, reg); + if (ret < 0) + return; ret = i2c_master_send(client, buf, cnt + 1); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); } -static uint8_t +static int reg_read(struct tda998x_priv *priv, uint16_t reg) { uint8_t val = 0; - reg_read_range(priv, reg, &val, sizeof(val)); + int ret; + + ret = reg_read_range(priv, reg, &val, sizeof(val)); + if (ret < 0) + return ret; return val; } @@ -434,7 +445,9 @@ reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val) uint8_t buf[] = {REG2ADDR(reg), val}; int ret; - set_page(priv, reg); + ret = set_page(priv, reg); + if (ret < 0) + return; ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); if (ret < 0) @@ -448,7 +461,9 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val) uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; int ret; - set_page(priv, reg); + ret = set_page(priv, reg); + if (ret < 0) + return; ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); if (ret < 0) @@ -458,13 +473,21 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val) static void reg_set(struct tda998x_priv *priv, uint16_t reg, uint8_t val) { - reg_write(priv, reg, reg_read(priv, reg) | val); + int old_val; + + old_val = reg_read(priv, reg); + if (old_val >= 0) + reg_write(priv, reg, old_val | val); } static void reg_clear(struct tda998x_priv *priv, uint16_t reg, uint8_t val) { - reg_write(priv, reg, reg_read(priv, reg) & ~val); + int old_val; + + old_val = reg_read(priv, reg); + if (old_val >= 0) + reg_write(priv, reg, old_val & ~val); } static void @@ -970,8 +993,10 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) /* wait for block read to complete: */ for (i = 100; i > 0; i--) { - uint8_t val = reg_read(priv, REG_INT_FLAGS_2); - if (val & INT_FLAGS_2_EDID_BLK_RD) + ret = reg_read(priv, REG_INT_FLAGS_2); + if (ret < 0) + return ret; + if (ret & INT_FLAGS_2_EDID_BLK_RD) break; msleep(1); } @@ -1134,6 +1159,7 @@ tda998x_encoder_init(struct i2c_client *client, struct drm_encoder_slave *encoder_slave) { struct tda998x_priv *priv; + int ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -1158,8 +1184,11 @@ tda998x_encoder_init(struct i2c_client *client, tda998x_reset(priv); /* read version: */ - priv->rev = reg_read(priv, REG_VERSION_LSB) | - reg_read(priv, REG_VERSION_MSB) << 8; + ret = reg_read(priv, REG_VERSION_LSB) | + (reg_read(priv, REG_VERSION_MSB) << 8); + if (ret < 0) + goto fail; + priv->rev = ret; /* mask off feature bits: */ priv->rev &= ~0x30; /* not-hdcp and not-scalar bit */