lkml.org 
[lkml]   [2022]   [Jan]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 2/2] drivers/misc: add transfer ioctl for HPS
Date
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@chromium.org>
---

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@chromium.org>
R: Evan Benn <evanbenn@chromium.org>
S: Maintained
F: drivers/misc/hps-i2c.c
+F: include/uapi/linux/hps.h

HSI SUBSYSTEM
M: Sebastian Reichel <sre@kernel.org>
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
\
 
 \ /
  Last update: 2022-01-27 09:37    [W:0.121 / U:0.284 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site