aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common/src/linux_i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi/common/src/linux_i2c.c')
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2c.c118
1 files changed, 114 insertions, 4 deletions
diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
index bdf381f8a032..60f7737cf6ec 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2c.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -24,9 +24,6 @@
*
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -167,6 +164,116 @@ lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
return (0);
}
+static int i2c_check_for_quirks(struct i2c_adapter *adapter,
+ struct iic_msg *msgs, uint32_t nmsgs)
+{
+ const struct i2c_adapter_quirks *quirks;
+ device_t dev;
+ int i, max_nmsgs;
+ bool check_len;
+
+ dev = adapter->dev.parent->bsddev;
+ quirks = adapter->quirks;
+ if (quirks == NULL)
+ return (0);
+
+ check_len = true;
+ max_nmsgs = quirks->max_num_msgs;
+
+ if (quirks->flags & I2C_AQ_COMB) {
+ max_nmsgs = 2;
+
+ if (nmsgs == 2) {
+ if (quirks->flags & I2C_AQ_COMB_WRITE_FIRST &&
+ msgs[0].flags & IIC_M_RD) {
+ device_printf(dev,
+ "Error: "
+ "first combined message must be write\n");
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_COMB_READ_SECOND &&
+ !(msgs[1].flags & IIC_M_RD)) {
+ device_printf(dev,
+ "Error: "
+ "second combined message must be read\n");
+ return (EOPNOTSUPP);
+ }
+
+ if (quirks->flags & I2C_AQ_COMB_SAME_ADDR &&
+ msgs[0].slave != msgs[1].slave) {
+ device_printf(dev,
+ "Error: "
+ "combined message must be use the same "
+ "address\n");
+ return (EOPNOTSUPP);
+ }
+
+ if (quirks->max_comb_1st_msg_len &&
+ msgs[0].len > quirks->max_comb_1st_msg_len) {
+ device_printf(dev,
+ "Error: "
+ "message too long: %hu > %hu max\n",
+ msgs[0].len,
+ quirks->max_comb_1st_msg_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->max_comb_2nd_msg_len &&
+ msgs[1].len > quirks->max_comb_2nd_msg_len) {
+ device_printf(dev,
+ "Error: "
+ "message too long: %hu > %hu max\n",
+ msgs[1].len,
+ quirks->max_comb_2nd_msg_len);
+ return (EOPNOTSUPP);
+ }
+
+ check_len = false;
+ }
+ }
+
+ if (max_nmsgs && nmsgs > max_nmsgs) {
+ device_printf(dev,
+ "Error: too many messages: %d > %d max\n",
+ nmsgs, max_nmsgs);
+ return (EOPNOTSUPP);
+ }
+
+ for (i = 0; i < nmsgs; i++) {
+ if (msgs[i].flags & IIC_M_RD) {
+ if (check_len && quirks->max_read_len &&
+ msgs[i].len > quirks->max_read_len) {
+ device_printf(dev,
+ "Error: "
+ "message %d too long: %hu > %hu max\n",
+ i, msgs[i].len, quirks->max_read_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_NO_ZERO_LEN_READ &&
+ msgs[i].len == 0) {
+ device_printf(dev,
+ "Error: message %d of length 0\n", i);
+ return (EOPNOTSUPP);
+ }
+ } else {
+ if (check_len && quirks->max_write_len &&
+ msgs[i].len > quirks->max_write_len) {
+ device_printf(dev,
+ "Message %d too long: %hu > %hu max\n",
+ i, msgs[i].len, quirks->max_write_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_NO_ZERO_LEN_WRITE &&
+ msgs[i].len == 0) {
+ device_printf(dev,
+ "Error: message %d of length 0\n", i);
+ return (EOPNOTSUPP);
+ }
+ }
+ }
+
+ return (0);
+}
+
static int
lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{
@@ -177,13 +284,16 @@ lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
sc = device_get_softc(dev);
if (sc->adapter == NULL)
return (ENXIO);
+ ret = i2c_check_for_quirks(sc->adapter, msgs, nmsgs);
+ if (ret != 0)
+ return (ret);
linux_set_current(curthread);
linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,
M_DEVBUF, M_WAITOK | M_ZERO);
for (i = 0; i < nmsgs; i++) {
- linux_msgs[i].addr = msgs[i].slave;
+ linux_msgs[i].addr = msgs[i].slave >> 1;
linux_msgs[i].len = msgs[i].len;
linux_msgs[i].buf = msgs[i].buf;
if (msgs[i].flags & IIC_M_RD) {