aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Schaefer <fschaefer.oss@googlemail.com>2013-01-03 12:27:03 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-01-04 22:16:01 -0500
commit2fcc82d8831a74afd55c3cb898beb9fde5f2a1fd (patch)
tree3074acd519b4d90172dd3360edd06843f81da737
parentf5ae371aca34bd0660a75f8838198466e9d5166c (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.c104
-rw-r--r--drivers/media/usb/em28xx/em28xx.h2
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 */
96static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) 98static 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 */
131static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) 154static 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
199enum em28xx_mode { 199enum em28xx_mode {
200 EM28XX_SUSPEND, 200 EM28XX_SUSPEND,