aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linux
diff options
context:
space:
mode:
authorYAO, Xin <mr.yaoxin@outlook.com>2026-04-13 12:28:48 +0000
committerPouria Mousavizadeh Tehrani <pouria@FreeBSD.org>2026-04-13 12:31:47 +0000
commit26740e8f80da17c78bee6fa322e6bb1f2669be5c (patch)
tree3e501cb2ff62729b5f9b3352bce54ab2e36ee119 /sys/compat/linux
parente9fc0c538264355bd3fd9120c650078281c2a290 (diff)
Diffstat (limited to 'sys/compat/linux')
-rw-r--r--sys/compat/linux/linux_ioctl.c115
-rw-r--r--sys/compat/linux/linux_ioctl.h24
2 files changed, 139 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index d2fa0331026b..b68e950a2dcf 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -43,6 +43,7 @@
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/proc.h>
+#include <sys/vnode.h>
#include <sys/sbuf.h>
#include <sys/sockio.h>
#include <sys/soundcard.h>
@@ -59,6 +60,7 @@
#include <dev/evdev/input.h>
#include <dev/hid/hidraw.h>
+#include <dev/iicbus/iic.h>
#include <dev/usb/usb_ioctl.h>
#ifdef COMPAT_LINUX32
@@ -100,6 +102,7 @@ DEFINE_LINUX_IOCTL_SET(vfat, VFAT);
DEFINE_LINUX_IOCTL_SET(console, CONSOLE);
DEFINE_LINUX_IOCTL_SET(hdio, HDIO);
DEFINE_LINUX_IOCTL_SET(disk, DISK);
+DEFINE_LINUX_IOCTL_SET(i2c, I2C);
DEFINE_LINUX_IOCTL_SET(socket, SOCKET);
DEFINE_LINUX_IOCTL_SET(sound, SOUND);
DEFINE_LINUX_IOCTL_SET(termio, TERMIO);
@@ -160,6 +163,18 @@ struct linux_hd_big_geometry {
uint32_t start;
};
+struct linux_i2c_msg {
+ uint16_t addr;
+ uint16_t flags;
+ uint16_t len;
+ l_uintptr_t buf;
+};
+
+struct linux_i2c_rdwr_data {
+ l_uintptr_t msgs;
+ l_uint nmsgs;
+};
+
static int
linux_ioctl_hdio(struct thread *td, struct linux_ioctl_args *args)
{
@@ -3640,6 +3655,106 @@ linux_ioctl_nvme(struct thread *td, struct linux_ioctl_args *args)
#endif
static int
+linux_ioctl_i2c(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct linux_i2c_rdwr_data lrdwr;
+ struct linux_i2c_msg *lmsgs = NULL;
+ struct iic_rdwr_data rdwr;
+ struct iic_msg *msgs = NULL;
+ struct file *fp;
+ iic_linux_rdwr_t *linux_rdwr;
+ l_ulong funcs;
+ uint16_t lflags;
+ uint8_t addr;
+ int error;
+ l_uint i;
+
+ error = fget(td, args->fd, &cap_ioctl_rights, &fp);
+ if (error != 0)
+ return (error);
+
+ linux_rdwr = NULL;
+ if (fp->f_type == DTYPE_VNODE && fp->f_vnode != NULL &&
+ fp->f_vnode->v_rdev != NULL)
+ linux_rdwr = (iic_linux_rdwr_t *)fp->f_vnode->v_rdev->si_drv2;
+
+ switch (args->cmd & 0xffff) {
+ case LINUX_I2C_RETRIES:
+ case LINUX_I2C_TIMEOUT:
+ case LINUX_I2C_PEC:
+ error = 0;
+ break;
+ case LINUX_I2C_TENBIT:
+ error = (args->arg == 0) ? 0 : ENOTSUP;
+ break;
+ case LINUX_I2C_FUNCS:
+ funcs = LINUX_I2C_FUNC_I2C | LINUX_I2C_FUNC_NOSTART;
+ error = copyout(&funcs, (void *)args->arg, sizeof(funcs));
+ break;
+ case LINUX_I2C_SLAVE:
+ case LINUX_I2C_SLAVE_FORCE:
+ if (args->arg > 0x7f) {
+ error = EINVAL;
+ break;
+ }
+ addr = (uint8_t)(args->arg << 1);
+ error = fo_ioctl(fp, I2CSADDR, (caddr_t)&addr, td->td_ucred, td);
+ break;
+ case LINUX_I2C_RDWR:
+ error = copyin((void *)args->arg, &lrdwr, sizeof(lrdwr));
+ if (error != 0)
+ break;
+ if (lrdwr.nmsgs > IIC_RDRW_MAX_MSGS) {
+ error = EINVAL;
+ break;
+ }
+ lmsgs = malloc(sizeof(*lmsgs) * lrdwr.nmsgs, M_TEMP, M_WAITOK);
+ msgs = malloc(sizeof(*msgs) * lrdwr.nmsgs, M_TEMP, M_WAITOK);
+ error = copyin((void *)(uintptr_t)lrdwr.msgs, lmsgs,
+ sizeof(*lmsgs) * lrdwr.nmsgs);
+ if (error != 0)
+ break;
+ for (i = 0; i < lrdwr.nmsgs; i++) {
+ lflags = lmsgs[i].flags;
+ if (lmsgs[i].addr > 0x7f || (lflags & LINUX_I2C_M_TEN) != 0) {
+ error = ENOTSUP;
+ break;
+ }
+ if ((lflags & ~(LINUX_I2C_M_RD | LINUX_I2C_M_NOSTART)) != 0) {
+ error = ENOTSUP;
+ break;
+ }
+ msgs[i].slave = lmsgs[i].addr << 1;
+ msgs[i].flags = (lflags & LINUX_I2C_M_RD) ? IIC_M_RD : IIC_M_WR;
+ if ((lflags & LINUX_I2C_M_NOSTART) != 0)
+ msgs[i].flags |= IIC_M_NOSTART;
+ msgs[i].len = lmsgs[i].len;
+ msgs[i].buf = (uint8_t *)(uintptr_t)lmsgs[i].buf;
+ }
+ if (error == 0) {
+ if (linux_rdwr == NULL) {
+ error = ENOTTY;
+ } else {
+ rdwr.msgs = msgs;
+ rdwr.nmsgs = lrdwr.nmsgs;
+ error = linux_rdwr(fp, &rdwr, fp->f_flag, td);
+ if (error == 0)
+ td->td_retval[0] = lrdwr.nmsgs;
+ }
+ }
+ break;
+ case LINUX_I2C_SMBUS:
+ default:
+ error = ENOTSUP;
+ break;
+ }
+ free(msgs, M_TEMP);
+ free(lmsgs, M_TEMP);
+ fdrop(fp, td);
+ return (error);
+}
+
+static int
linux_ioctl_hidraw(struct thread *td, struct linux_ioctl_args *args)
{
int len = (args->cmd & 0x3fff0000) >> 16;
diff --git a/sys/compat/linux/linux_ioctl.h b/sys/compat/linux/linux_ioctl.h
index 116a4e676228..769f56b7de51 100644
--- a/sys/compat/linux/linux_ioctl.h
+++ b/sys/compat/linux/linux_ioctl.h
@@ -72,6 +72,30 @@
#define LINUX_IOCTL_HDIO_MAX LINUX_HDIO_GET_GEO_BIG
/*
+ * i2c
+ */
+#define LINUX_I2C_RETRIES 0x0701
+#define LINUX_I2C_TIMEOUT 0x0702
+#define LINUX_I2C_SLAVE 0x0703
+#define LINUX_I2C_TENBIT 0x0704
+#define LINUX_I2C_FUNCS 0x0705
+#define LINUX_I2C_SLAVE_FORCE 0x0706
+#define LINUX_I2C_RDWR 0x0707
+#define LINUX_I2C_PEC 0x0708
+#define LINUX_I2C_SMBUS 0x0720
+
+#define LINUX_IOCTL_I2C_MIN LINUX_I2C_RETRIES
+#define LINUX_IOCTL_I2C_MAX LINUX_I2C_SMBUS
+
+#define LINUX_I2C_M_RD 0x0001
+#define LINUX_I2C_M_TEN 0x0010
+#define LINUX_I2C_M_NOSTART 0x4000
+
+#define LINUX_I2C_FUNC_I2C 0x00000001UL
+#define LINUX_I2C_FUNC_10BIT_ADDR 0x00000002UL
+#define LINUX_I2C_FUNC_NOSTART 0x00000010UL
+
+/*
* cdrom
*/
#define LINUX_CDROMPAUSE 0x5301