mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
HID: mcp2221: Handle reads greater than 60 bytes
When a user requests more than 60 bytes of data the MCP2221 must chunk the data in chunks up to 60 bytes long (see command/response code 0x40 in the datasheet). In order to signal that the device has more data the (undocumented) byte at byte index 2 of the Get I2C Data response uses the value 0x54. This contrasts with the case for the final data chunk where the value returned is 0x55 (MCP2221_I2C_READ_COMPL). The fact that 0x55 was not returned in the response was interpreted by the driver as a failure meaning that all reads of more than 60 bytes would fail. Add support for reads that are split over multiple chunks by looking for the response code indicating that more data is expected and continuing the read as the code intended. Some timing delays are required to ensure the chip has time to refill its FIFO as data is read in from the I2C bus. This timing has been tested in my system when configured for bus speeds of 50KHz, 100KHz, and 400KHz and operates well. Signed-off-by: Hamish Martin <hamish.martin@alliedtelesis.co.nz> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
02a4675360
commit
2682468671
@ -49,6 +49,7 @@ enum {
|
||||
MCP2221_I2C_MASK_ADDR_NACK = 0x40,
|
||||
MCP2221_I2C_WRADDRL_SEND = 0x21,
|
||||
MCP2221_I2C_ADDR_NACK = 0x25,
|
||||
MCP2221_I2C_READ_PARTIAL = 0x54,
|
||||
MCP2221_I2C_READ_COMPL = 0x55,
|
||||
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
|
||||
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
|
||||
@ -297,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
|
||||
{
|
||||
int ret;
|
||||
u16 total_len;
|
||||
int retries = 0;
|
||||
|
||||
mcp->txbuf[0] = type;
|
||||
if (msg) {
|
||||
@ -320,20 +322,31 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
|
||||
mcp->rxbuf_idx = 0;
|
||||
|
||||
do {
|
||||
/* Wait for the data to be read by the device */
|
||||
usleep_range(980, 1000);
|
||||
|
||||
memset(mcp->txbuf, 0, 4);
|
||||
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
|
||||
|
||||
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mcp_chk_last_cmd_status_free_bus(mcp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(980, 1000);
|
||||
if (ret) {
|
||||
if (retries < 5) {
|
||||
/* The data wasn't ready to read.
|
||||
* Wait a bit longer and try again.
|
||||
*/
|
||||
usleep_range(90, 100);
|
||||
retries++;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
retries = 0;
|
||||
}
|
||||
} while (mcp->rxbuf_idx < total_len);
|
||||
|
||||
usleep_range(980, 1000);
|
||||
ret = mcp_chk_last_cmd_status_free_bus(mcp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -799,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
|
||||
mcp->status = -EIO;
|
||||
break;
|
||||
}
|
||||
if (data[2] == MCP2221_I2C_READ_COMPL) {
|
||||
if (data[2] == MCP2221_I2C_READ_COMPL ||
|
||||
data[2] == MCP2221_I2C_READ_PARTIAL) {
|
||||
buf = mcp->rxbuf;
|
||||
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
|
||||
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
|
||||
|
Loading…
Reference in New Issue
Block a user