diff options
author | Frank Schaefer <fschaefer.oss@googlemail.com> | 2013-01-03 12:27:03 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-01-04 22:16:01 -0500 |
commit | 2fcc82d8831a74afd55c3cb898beb9fde5f2a1fd (patch) | |
tree | 3074acd519b4d90172dd3360edd06843f81da737 | |
parent | f5ae371aca34bd0660a75f8838198466e9d5166c (diff) |
[media] em28xx: fix two severe bugs in function em2800_i2c_recv_bytes()
Function em2800_i2c_recv_bytes() has 2 severe bugs:
1) It does not wait for the i2c read to complete before reading the received message content from the bridge registers.
2) Reading more than 1 byte doesn't work
The former can result in data corruption, the latter always does.
The rewritten code also superseds the content of function
em2800_i2c_check_for_device().
Tested with device "Terratec Cinergy 200 USB".
[mchehab@redhat.com: Fix CodingStyle issues]
Signed-off-by: Frank Schäfer <fschaefer.oss@googlemail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-i2c.c | 104 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx.h | 2 |
2 files changed, 58 insertions, 48 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index c508c1297a26..67a8e623dd8c 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c | |||
@@ -73,12 +73,14 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) | |||
73 | if (len > 3) | 73 | if (len > 3) |
74 | b2[0] = buf[3]; | 74 | b2[0] = buf[3]; |
75 | 75 | ||
76 | /* trigger write */ | ||
76 | ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); | 77 | ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); |
77 | if (ret != 2 + len) { | 78 | if (ret != 2 + len) { |
78 | em28xx_warn("writing to i2c device failed (error=%i)\n", ret); | 79 | em28xx_warn("writing to i2c device failed (error=%i)\n", ret); |
79 | return -EIO; | 80 | return -EIO; |
80 | } | 81 | } |
81 | for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; | 82 | /* wait for completion */ |
83 | for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; | ||
82 | write_timeout -= 5) { | 84 | write_timeout -= 5) { |
83 | ret = dev->em28xx_read_reg(dev, 0x05); | 85 | ret = dev->em28xx_read_reg(dev, 0x05); |
84 | if (ret == 0x80 + len - 1) | 86 | if (ret == 0x80 + len - 1) |
@@ -90,66 +92,74 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) | |||
90 | } | 92 | } |
91 | 93 | ||
92 | /* | 94 | /* |
93 | * em2800_i2c_check_for_device() | 95 | * em2800_i2c_recv_bytes() |
94 | * check if there is a i2c_device at the supplied address | 96 | * read up to 4 bytes from the em2800 i2c device |
95 | */ | 97 | */ |
96 | static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) | 98 | static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) |
97 | { | 99 | { |
98 | u8 msg; | 100 | u8 buf2[4]; |
99 | int ret; | 101 | int ret; |
100 | int write_timeout; | 102 | int read_timeout; |
101 | msg = addr; | 103 | int i; |
102 | ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); | 104 | |
103 | if (ret < 0) { | 105 | if (len < 1 || len > 4) |
104 | em28xx_warn("setting i2c device address failed (error=%i)\n", | 106 | return -EOPNOTSUPP; |
105 | ret); | 107 | |
106 | return ret; | 108 | /* trigger read */ |
107 | } | 109 | buf2[1] = 0x84 + len - 1; |
108 | msg = 0x84; | 110 | buf2[0] = addr; |
109 | ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); | 111 | ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2); |
110 | if (ret < 0) { | 112 | if (ret != 2) { |
111 | em28xx_warn("preparing i2c read failed (error=%i)\n", ret); | 113 | em28xx_warn("failed to trigger read from i2c address 0x%x " |
112 | return ret; | 114 | "(error=%i)\n", addr, ret); |
115 | return (ret < 0) ? ret : -EIO; | ||
113 | } | 116 | } |
114 | for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; | ||
115 | write_timeout -= 5) { | ||
116 | unsigned reg = dev->em28xx_read_reg(dev, 0x5); | ||
117 | 117 | ||
118 | if (reg == 0x94) | 118 | /* wait for completion */ |
119 | for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0; | ||
120 | read_timeout -= 5) { | ||
121 | ret = dev->em28xx_read_reg(dev, 0x05); | ||
122 | if (ret == 0x84 + len - 1) { | ||
123 | break; | ||
124 | } else if (ret == 0x94 + len - 1) { | ||
119 | return -ENODEV; | 125 | return -ENODEV; |
120 | else if (reg == 0x84) | 126 | } else if (ret < 0) { |
121 | return 0; | 127 | em28xx_warn("failed to get i2c transfer status from " |
128 | "bridge register (error=%i)\n", ret); | ||
129 | return ret; | ||
130 | } | ||
122 | msleep(5); | 131 | msleep(5); |
123 | } | 132 | } |
124 | return -ENODEV; | 133 | if (ret != 0x84 + len - 1) |
134 | em28xx_warn("read from i2c device at 0x%x timed out\n", addr); | ||
135 | |||
136 | /* get the received message */ | ||
137 | ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); | ||
138 | if (ret != len) { | ||
139 | em28xx_warn("reading from i2c device at 0x%x failed: " | ||
140 | "couldn't get the received message from the bridge " | ||
141 | "(error=%i)\n", addr, ret); | ||
142 | return (ret < 0) ? ret : -EIO; | ||
143 | } | ||
144 | for (i = 0; i < len; i++) | ||
145 | buf[i] = buf2[len - 1 - i]; | ||
146 | |||
147 | return ret; | ||
125 | } | 148 | } |
126 | 149 | ||
127 | /* | 150 | /* |
128 | * em2800_i2c_recv_bytes() | 151 | * em2800_i2c_check_for_device() |
129 | * read from the i2c device | 152 | * check if there is an i2c device at the supplied address |
130 | */ | 153 | */ |
131 | static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) | 154 | static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) |
132 | { | 155 | { |
156 | u8 buf; | ||
133 | int ret; | 157 | int ret; |
134 | 158 | ||
135 | if (len < 1 || len > 4) | 159 | ret = em2800_i2c_recv_bytes(dev, addr, &buf, 1); |
136 | return -EOPNOTSUPP; | 160 | if (ret == 1) |
137 | 161 | return 0; | |
138 | /* check for the device and set i2c read address */ | 162 | return (ret < 0) ? ret : -EIO; |
139 | ret = em2800_i2c_check_for_device(dev, addr); | ||
140 | if (ret) { | ||
141 | em28xx_warn | ||
142 | ("preparing read at i2c address 0x%x failed (error=%i)\n", | ||
143 | addr, ret); | ||
144 | return ret; | ||
145 | } | ||
146 | ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); | ||
147 | if (ret < 0) { | ||
148 | em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", | ||
149 | addr, ret); | ||
150 | return ret; | ||
151 | } | ||
152 | return ret; | ||
153 | } | 163 | } |
154 | 164 | ||
155 | /* | 165 | /* |
@@ -167,7 +177,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, | |||
167 | wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); | 177 | wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); |
168 | 178 | ||
169 | /* Seems to be required after a write */ | 179 | /* Seems to be required after a write */ |
170 | for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; | 180 | for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; |
171 | write_timeout -= 5) { | 181 | write_timeout -= 5) { |
172 | ret = dev->em28xx_read_reg(dev, 0x05); | 182 | ret = dev->em28xx_read_reg(dev, 0x05); |
173 | if (!ret) | 183 | if (!ret) |
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f891a28706f5..2aa4b8472a62 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h | |||
@@ -194,7 +194,7 @@ | |||
194 | */ | 194 | */ |
195 | 195 | ||
196 | /* time in msecs to wait for i2c writes to finish */ | 196 | /* time in msecs to wait for i2c writes to finish */ |
197 | #define EM2800_I2C_WRITE_TIMEOUT 20 | 197 | #define EM2800_I2C_XFER_TIMEOUT 20 |
198 | 198 | ||
199 | enum em28xx_mode { | 199 | enum em28xx_mode { |
200 | EM28XX_SUSPEND, | 200 | EM28XX_SUSPEND, |