[PATCH 2/2] drivers/misc: add transfer ioctl for HPS

From: Sami Kyöstilä
Date: Thu Jan 27 2022 - 03:36:39 EST


This patch adds an ioctl operation for sending and receiving data from
the ChromeOS snooping protection sensor (a.k.a., HPS). This allows
userspace programs to perform a combined read/write I2C transaction
through a single syscall.

The I2C wire protocol for the device is documented at:

https://chromium.googlesource.com/chromiumos/platform/hps-firmware/+/
refs/heads/main/docs/host_device_i2c_protocol.md

Signed-off-by: Sami Kyöstilä <skyostil@xxxxxxxxxxxx>
---

MAINTAINERS | 1 +
drivers/misc/hps-i2c.c | 81 ++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/hps.h | 20 ++++++++++
3 files changed, 102 insertions(+)
create mode 100644 include/uapi/linux/hps.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9dea4b8c2ab5..d5fc066fdbc2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8803,6 +8803,7 @@ M: Sami Kyöstilä <skyostil@xxxxxxxxxxxx>
R: Evan Benn <evanbenn@xxxxxxxxxxxx>
S: Maintained
F: drivers/misc/hps-i2c.c
+F: include/uapi/linux/hps.h

HSI SUBSYSTEM
M: Sebastian Reichel <sre@xxxxxxxxxx>
diff --git a/drivers/misc/hps-i2c.c b/drivers/misc/hps-i2c.c
index fe9f073b0352..748ead49d678 100644
--- a/drivers/misc/hps-i2c.c
+++ b/drivers/misc/hps-i2c.c
@@ -17,9 +17,11 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <uapi/linux/hps.h>

#define HPS_ACPI_ID "GOOG0020"
#define HPS_MAX_DEVICES 1
+#define HPS_MAX_MSG_SIZE 8192

struct hps_drvdata {
struct i2c_client *client;
@@ -60,6 +62,8 @@ static int hps_open(struct inode *inode, struct file *file)
ret = pm_runtime_get_sync(dev);
if (ret < 0)
goto pm_get_fail;
+
+ file->private_data = hps->client;
return 0;

pm_get_fail:
@@ -84,10 +88,87 @@ static int hps_release(struct inode *inode, struct file *file)
return ret;
}

+static int hps_do_ioctl_transfer(struct i2c_client *client,
+ struct hps_transfer_ioctl_data *args)
+{
+ int ret;
+ int nmsg = 0;
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = client->flags,
+ },
+ {
+ .addr = client->addr,
+ .flags = client->flags,
+ },
+ };
+
+ if (args->isize) {
+ msgs[nmsg].len = args->isize;
+ msgs[nmsg].buf = memdup_user(args->ibuf, args->isize);
+ if (IS_ERR(msgs[nmsg].buf)) {
+ ret = PTR_ERR(msgs[nmsg].buf);
+ goto memdup_fail;
+ }
+ nmsg++;
+ }
+
+ if (args->osize) {
+ msgs[nmsg].len = args->osize;
+ msgs[nmsg].buf = memdup_user(args->obuf, args->osize);
+ msgs[nmsg].flags |= I2C_M_RD;
+ if (IS_ERR(msgs[nmsg].buf)) {
+ ret = PTR_ERR(msgs[nmsg].buf);
+ goto memdup_fail;
+ }
+ nmsg++;
+ }
+
+ ret = i2c_transfer(client->adapter, &msgs[0], nmsg);
+ if (ret > 0 && args->osize) {
+ if (copy_to_user(args->obuf, msgs[nmsg - 1].buf, ret))
+ ret = -EFAULT;
+ }
+
+memdup_fail:
+ while (nmsg > 0)
+ kfree(msgs[--nmsg].buf);
+ return ret;
+}
+
+static long hps_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_client *client = file->private_data;
+
+ switch (cmd) {
+ case HPS_IOC_TRANSFER: {
+ struct hps_transfer_ioctl_data args;
+
+ if (copy_from_user(&args,
+ (struct hps_transfer_ioctl_data __user *)arg,
+ sizeof(args))) {
+ return -EFAULT;
+ }
+
+ if (!args.isize && !args.osize)
+ return -EINVAL;
+ if (args.isize > HPS_MAX_MSG_SIZE || args.osize > HPS_MAX_MSG_SIZE)
+ return -EINVAL;
+
+ return hps_do_ioctl_transfer(client, &args);
+ }
+ default:
+ return -EFAULT;
+ }
+}
+
+
const struct file_operations hps_fops = {
.owner = THIS_MODULE,
.open = hps_open,
.release = hps_release,
+ .unlocked_ioctl = hps_ioctl,
};

static int hps_i2c_probe(struct i2c_client *client)
diff --git a/include/uapi/linux/hps.h b/include/uapi/linux/hps.h
new file mode 100644
index 000000000000..2c1bd174cd02
--- /dev/null
+++ b/include/uapi/linux/hps.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright 2022 Google LLC.
+ */
+
+#ifndef _UAPI_HPS_H
+#define _UAPI_HPS_H
+
+#include <linux/types.h>
+
+#define HPS_IOC_TRANSFER _IOWR('h', 0x01, struct hps_transfer_ioctl_data)
+
+struct hps_transfer_ioctl_data {
+ __u32 isize; /* Number of bytes to send */
+ unsigned char __user *ibuf; /* Input buffer */
+ __u32 osize; /* Number of bytes to receive */
+ unsigned char __user *obuf; /* Output buffer */
+};
+
+#endif /* _UAPI_HPS_H */
--
2.35.0.rc0.227.g00780c9af4-goog