diff options
| author | YAO, Xin <mr.yaoxin@outlook.com> | 2026-04-13 12:28:48 +0000 |
|---|---|---|
| committer | Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> | 2026-04-13 12:31:47 +0000 |
| commit | 26740e8f80da17c78bee6fa322e6bb1f2669be5c (patch) | |
| tree | 3e501cb2ff62729b5f9b3352bce54ab2e36ee119 /sys/compat/linux | |
| parent | e9fc0c538264355bd3fd9120c650078281c2a290 (diff) | |
Diffstat (limited to 'sys/compat/linux')
| -rw-r--r-- | sys/compat/linux/linux_ioctl.c | 115 | ||||
| -rw-r--r-- | sys/compat/linux/linux_ioctl.h | 24 |
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 |
