Messages in this thread Patch in this message | | | From | Sami Kyöstilä <> | Subject | [PATCH 2/2] drivers/misc: add transfer ioctl for HPS | Date | Thu, 27 Jan 2022 19:35:45 +1100 |
| |
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
| |