|
@@ -429,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
|
|
return data_length + 4;
|
|
return data_length + 4;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data,
|
|
|
|
+ u8 data_length)
|
|
|
|
+{
|
|
|
|
+ struct cp2112_write_req_report *report = buf;
|
|
|
|
+
|
|
|
|
+ if (data_length > sizeof(report->data))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ report->report = CP2112_DATA_WRITE_REQUEST;
|
|
|
|
+ report->slave_address = slave_address << 1;
|
|
|
|
+ report->length = data_length;
|
|
|
|
+ memcpy(report->data, data, data_length);
|
|
|
|
+ return data_length + 3;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|
|
|
+ int num)
|
|
|
|
+{
|
|
|
|
+ struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
|
|
|
|
+ struct hid_device *hdev = dev->hdev;
|
|
|
|
+ u8 buf[64];
|
|
|
|
+ ssize_t count;
|
|
|
|
+ unsigned int retries;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ hid_dbg(hdev, "I2C %d messages\n", num);
|
|
|
|
+
|
|
|
|
+ if (num != 1) {
|
|
|
|
+ hid_err(hdev,
|
|
|
|
+ "Multi-message I2C transactions not supported\n");
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (msgs->flags & I2C_M_RD)
|
|
|
|
+ count = cp2112_read_req(buf, msgs->addr, msgs->len);
|
|
|
|
+ else
|
|
|
|
+ count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf,
|
|
|
|
+ msgs->len);
|
|
|
|
+
|
|
|
|
+ if (count < 0)
|
|
|
|
+ return count;
|
|
|
|
+
|
|
|
|
+ ret = hid_hw_power(hdev, PM_HINT_FULLON);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ hid_err(hdev, "power management error: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ hid_warn(hdev, "Error starting transaction: %d\n", ret);
|
|
|
|
+ goto power_normal;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
|
|
|
|
+ ret = cp2112_xfer_status(dev);
|
|
|
|
+ if (-EBUSY == ret)
|
|
|
|
+ continue;
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto power_normal;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (XFER_STATUS_RETRIES <= retries) {
|
|
|
|
+ hid_warn(hdev, "Transfer timed out, cancelling.\n");
|
|
|
|
+ buf[0] = CP2112_CANCEL_TRANSFER;
|
|
|
|
+ buf[1] = 0x01;
|
|
|
|
+
|
|
|
|
+ ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ hid_warn(hdev, "Error cancelling transaction: %d\n",
|
|
|
|
+ ret);
|
|
|
|
+
|
|
|
|
+ ret = -ETIMEDOUT;
|
|
|
|
+ goto power_normal;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!(msgs->flags & I2C_M_RD))
|
|
|
|
+ goto finish;
|
|
|
|
+
|
|
|
|
+ ret = cp2112_read(dev, msgs->buf, msgs->len);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto power_normal;
|
|
|
|
+ if (ret != msgs->len) {
|
|
|
|
+ hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ goto power_normal;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+finish:
|
|
|
|
+ /* return the number of transferred messages */
|
|
|
|
+ ret = 1;
|
|
|
|
+
|
|
|
|
+power_normal:
|
|
|
|
+ hid_hw_power(hdev, PM_HINT_NORMAL);
|
|
|
|
+ hid_dbg(hdev, "I2C transfer finished: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
|
|
static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
|
|
unsigned short flags, char read_write, u8 command,
|
|
unsigned short flags, char read_write, u8 command,
|
|
int size, union i2c_smbus_data *data)
|
|
int size, union i2c_smbus_data *data)
|
|
@@ -595,7 +694,8 @@ power_normal:
|
|
|
|
|
|
static u32 cp2112_functionality(struct i2c_adapter *adap)
|
|
static u32 cp2112_functionality(struct i2c_adapter *adap)
|
|
{
|
|
{
|
|
- return I2C_FUNC_SMBUS_BYTE |
|
|
|
|
|
|
+ return I2C_FUNC_I2C |
|
|
|
|
+ I2C_FUNC_SMBUS_BYTE |
|
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
|
I2C_FUNC_SMBUS_WORD_DATA |
|
|
I2C_FUNC_SMBUS_WORD_DATA |
|
|
I2C_FUNC_SMBUS_BLOCK_DATA |
|
|
I2C_FUNC_SMBUS_BLOCK_DATA |
|
|
@@ -605,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap)
|
|
}
|
|
}
|
|
|
|
|
|
static const struct i2c_algorithm smbus_algorithm = {
|
|
static const struct i2c_algorithm smbus_algorithm = {
|
|
|
|
+ .master_xfer = cp2112_i2c_xfer,
|
|
.smbus_xfer = cp2112_xfer,
|
|
.smbus_xfer = cp2112_xfer,
|
|
.functionality = cp2112_functionality,
|
|
.functionality = cp2112_functionality,
|
|
};
|
|
};
|