diff options
Diffstat (limited to 'sys/compat')
32 files changed, 1577 insertions, 303 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 4ec6dd452b32..a0b6118900ed 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -2649,7 +2649,7 @@ freebsd11_freebsd32_nstat(struct thread *td, if (error != 0) return (error); error = freebsd11_cvtnstat32(&sb, &nsb); - if (error != 0) + if (error == 0) error = copyout(&nsb, uap->ub, sizeof (nsb)); return (error); } diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c index 7ac48786c77b..786a486b9143 100644 --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -659,8 +659,7 @@ linprocfs_dopartitions(PFS_FILL_ARGS) int major, minor; g_topology_lock(); - sbuf_printf(sb, "major minor #blocks name rio rmerge rsect " - "ruse wio wmerge wsect wuse running use aveq\n"); + sbuf_printf(sb, "major minor #blocks name\n\n"); LIST_FOREACH(cp, &g_classes, class) { if (strcmp(cp->name, "DISK") == 0 || @@ -672,13 +671,10 @@ linprocfs_dopartitions(PFS_FILL_ARGS) major = 0; minor = 0; } - sbuf_printf(sb, "%d %d %lld %s " - "%d %d %d %d %d " - "%d %d %d %d %d %d\n", - major, minor, - (long long)pp->mediasize, pp->name, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0); + sbuf_printf(sb, "%4d %7d %10lld %s\n", + major, minor, + B2K((long long)pp->mediasize), + pp->name); } } } @@ -2026,23 +2022,26 @@ linprocfs_doauxv(PFS_FILL_ARGS) if (asb == NULL) return (ENOMEM); error = proc_getauxv(td, p, asb); - if (error == 0) - error = sbuf_finish(asb); + if (error != 0) + goto out; + error = sbuf_finish(asb); + if (error != 0) + goto out; resid = sbuf_len(asb) - uio->uio_offset; if (resid > uio->uio_resid) buflen = uio->uio_resid; else buflen = resid; - if (buflen > IOSIZE_MAX) - return (EINVAL); + if (buflen > IOSIZE_MAX) { + error = EINVAL; + goto out; + } if (buflen > maxphys) buflen = maxphys; - if (resid <= 0) - return (0); - - if (error == 0) + if (resid > 0) error = uiomove(sbuf_data(asb) + uio->uio_offset, buflen, uio); +out: sbuf_delete(asb); return (error); } 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 diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index b1a483ce611c..29b55ef60357 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -30,6 +30,7 @@ #include <sys/param.h> #include <sys/capsicum.h> +#include <sys/domain.h> #include <sys/filedesc.h> #include <sys/limits.h> #include <sys/malloc.h> @@ -52,6 +53,7 @@ #include <netinet/ip.h> #include <netinet/tcp.h> #ifdef INET6 +#include <netinet/icmp6.h> #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #endif @@ -594,10 +596,47 @@ linux_to_bsd_tcp_sockopt(int opt) return (-2); case LINUX_TCP_MD5SIG: return (TCP_MD5SIG); + case LINUX_TCP_USER_TIMEOUT: + return (TCP_MAXUNACKTIME); } return (-1); } +static u_int +linux_to_bsd_tcp_user_timeout(l_uint linux_timeout) +{ + + /* + * Linux exposes TCP_USER_TIMEOUT in milliseconds while + * TCP_MAXUNACKTIME uses whole seconds. Round up partial + * seconds so a non-zero Linux timeout never becomes zero. + */ + return (howmany(linux_timeout, 1000U)); +} + +static l_uint +bsd_to_linux_tcp_user_timeout(u_int bsd_timeout) +{ + + if (bsd_timeout > UINT_MAX / 1000U) + return (UINT_MAX); + + return (bsd_timeout * 1000U); +} + +#ifdef INET6 +static int +linux_to_bsd_icmp6_sockopt(int opt) +{ + + switch (opt) { + case LINUX_ICMP6_FILTER: + return (ICMP6_FILTER); + } + return (-1); +} +#endif + static int linux_to_bsd_msg_flags(int flags) { @@ -668,6 +707,20 @@ bsd_to_linux_ip_cmsg_type(int cmsg_type) return (-1); } +#ifdef INET6 +static int +bsd_to_linux_ip6_cmsg_type(int cmsg_type) +{ + switch (cmsg_type) { + case IPV6_2292HOPLIMIT: + return (LINUX_IPV6_2292HOPLIMIT); + case IPV6_HOPLIMIT: + return (LINUX_IPV6_HOPLIMIT); + } + return (-1); +} +#endif + static int bsd_to_linux_cmsg_type(struct proc *p, int cmsg_type, int cmsg_level) { @@ -675,6 +728,10 @@ bsd_to_linux_cmsg_type(struct proc *p, int cmsg_type, int cmsg_level) if (cmsg_level == IPPROTO_IP) return (bsd_to_linux_ip_cmsg_type(cmsg_type)); +#ifdef INET6 + if (cmsg_level == IPPROTO_IPV6) + return (bsd_to_linux_ip6_cmsg_type(cmsg_type)); +#endif if (cmsg_level != SOL_SOCKET) return (-1); @@ -2057,8 +2114,10 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args) struct proc *p = td->td_proc; struct linux_pemuldata *pem; l_timeval linux_tv; + l_uint linux_timeout; struct sockaddr *sa; struct timeval tv; + u_int bsd_timeout; socklen_t len; int error, level, name, val; @@ -2130,7 +2189,68 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args) break; case IPPROTO_TCP: name = linux_to_bsd_tcp_sockopt(args->optname); + switch (name) { + case TCP_MAXUNACKTIME: + if (args->optlen < sizeof(linux_timeout)) + return (EINVAL); + + error = copyin(PTRIN(args->optval), &linux_timeout, + sizeof(linux_timeout)); + if (error != 0) + return (error); + + bsd_timeout = linux_to_bsd_tcp_user_timeout( + linux_timeout); + return (kern_setsockopt(td, args->s, level, name, + &bsd_timeout, UIO_SYSSPACE, + sizeof(bsd_timeout))); + default: + break; + } + break; +#ifdef INET6 + case IPPROTO_RAW: { + struct file *fp; + struct socket *so; + int family; + + error = getsock(td, args->s, &cap_setsockopt_rights, &fp); + if (error != 0) + return (error); + so = fp->f_data; + family = so->so_proto->pr_domain->dom_family; + fdrop(fp, td); + + name = -1; + if (family == AF_INET6) { + name = linux_to_bsd_ip6_sockopt(args->optname); + if (name >= 0) + level = IPPROTO_IPV6; + } break; + } + case IPPROTO_ICMPV6: { + struct icmp6_filter f; + int i; + + name = linux_to_bsd_icmp6_sockopt(args->optname); + if (name != ICMP6_FILTER) + break; + + if (args->optlen != sizeof(f)) + return (EINVAL); + + error = copyin(PTRIN(args->optval), &f, sizeof(f)); + if (error) + return (error); + + /* Linux uses opposite values for pass/block in ICMPv6 */ + for (i = 0; i < nitems(f.icmp6_filt); i++) + f.icmp6_filt[i] = ~f.icmp6_filt[i]; + return (kern_setsockopt(td, args->s, IPPROTO_ICMPV6, + ICMP6_FILTER, &f, UIO_SYSSPACE, sizeof(f))); + } +#endif case SOL_NETLINK: name = args->optname; break; @@ -2279,10 +2399,12 @@ linux_getsockopt_so_linger(struct thread *td, int linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args) { + l_uint linux_timeout; l_timeval linux_tv; struct timeval tv; socklen_t tv_len, xulen, len; struct sockaddr *sa; + u_int bsd_timeout; struct xucred xu; struct l_ucred lxu; int error, level, name, newval; @@ -2373,7 +2495,72 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args) break; case IPPROTO_TCP: name = linux_to_bsd_tcp_sockopt(args->optname); + switch (name) { + case TCP_MAXUNACKTIME: + len = sizeof(bsd_timeout); + error = kern_getsockopt(td, args->s, level, name, + &bsd_timeout, UIO_SYSSPACE, &len); + if (error != 0) + return (error); + + linux_timeout = bsd_to_linux_tcp_user_timeout( + bsd_timeout); + return (linux_sockopt_copyout(td, &linux_timeout, + sizeof(linux_timeout), args)); + default: + break; + } + break; +#ifdef INET6 + case IPPROTO_RAW: { + struct file *fp; + struct socket *so; + int family; + + error = getsock(td, args->s, &cap_getsockopt_rights, &fp); + if (error != 0) + return (error); + so = fp->f_data; + family = so->so_proto->pr_domain->dom_family; + fdrop(fp, td); + + name = -1; + if (family == AF_INET6) { + name = linux_to_bsd_ip6_sockopt(args->optname); + if (name >= 0) + level = IPPROTO_IPV6; + } break; + } + case IPPROTO_ICMPV6: { + struct icmp6_filter f; + int i; + + name = linux_to_bsd_icmp6_sockopt(args->optname); + if (name != ICMP6_FILTER) + break; + + error = copyin(PTRIN(args->optlen), &len, sizeof(len)); + if (error) + return (error); + if (len != sizeof(f)) + return (EINVAL); + + error = kern_getsockopt(td, args->s, IPPROTO_ICMPV6, + ICMP6_FILTER, &f, UIO_SYSSPACE, &len); + if (error) + return (error); + + /* Linux uses opposite values for pass/block in ICMPv6 */ + for (i = 0; i < nitems(f.icmp6_filt); i++) + f.icmp6_filt[i] = ~f.icmp6_filt[i]; + error = copyout(&f, PTRIN(args->optval), len); + if (error) + return (error); + + return (copyout(&len, PTRIN(args->optlen), sizeof(socklen_t))); + } +#endif default: name = -1; break; diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h index 68176c3cc401..d30d68409496 100644 --- a/sys/compat/linux/linux_socket.h +++ b/sys/compat/linux/linux_socket.h @@ -322,6 +322,9 @@ int linux_accept(struct thread *td, struct linux_accept_args *args); #define LINUX_TCP_KEEPCNT 6 #define LINUX_TCP_INFO 11 #define LINUX_TCP_MD5SIG 14 +#define LINUX_TCP_USER_TIMEOUT 18 + +#define LINUX_ICMP6_FILTER 1 struct l_ifmap { l_ulong mem_start; diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index 03783d466211..6f96a219003b 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -295,26 +295,28 @@ struct l_statfs { #define LINUX_ZFS_SUPER_MAGIC 0x2FC12FC1 #define LINUX_DEVFS_SUPER_MAGIC 0x1373L #define LINUX_SHMFS_MAGIC 0x01021994 +#define LINUX_SYSFS_MAGIC 0x62656572 static long bsd_to_linux_ftype(const char *fstypename) { int i; static struct {const char *bsd_name; long linux_type;} b2l_tbl[] = { - {"ufs", LINUX_UFS_SUPER_MAGIC}, - {"zfs", LINUX_ZFS_SUPER_MAGIC}, - {"cd9660", LINUX_ISOFS_SUPER_MAGIC}, - {"nfs", LINUX_NFS_SUPER_MAGIC}, - {"ext2fs", LINUX_EXT2_SUPER_MAGIC}, - {"procfs", LINUX_PROC_SUPER_MAGIC}, - {"msdosfs", LINUX_MSDOS_SUPER_MAGIC}, - {"ntfs", LINUX_NTFS_SUPER_MAGIC}, - {"nwfs", LINUX_NCP_SUPER_MAGIC}, - {"hpfs", LINUX_HPFS_SUPER_MAGIC}, - {"coda", LINUX_CODA_SUPER_MAGIC}, - {"devfs", LINUX_DEVFS_SUPER_MAGIC}, - {"tmpfs", LINUX_SHMFS_MAGIC}, - {NULL, 0L}}; + {"ufs", LINUX_UFS_SUPER_MAGIC}, + {"zfs", LINUX_ZFS_SUPER_MAGIC}, + {"cd9660", LINUX_ISOFS_SUPER_MAGIC}, + {"nfs", LINUX_NFS_SUPER_MAGIC}, + {"ext2fs", LINUX_EXT2_SUPER_MAGIC}, + {"procfs", LINUX_PROC_SUPER_MAGIC}, + {"msdosfs", LINUX_MSDOS_SUPER_MAGIC}, + {"ntfs", LINUX_NTFS_SUPER_MAGIC}, + {"nwfs", LINUX_NCP_SUPER_MAGIC}, + {"hpfs", LINUX_HPFS_SUPER_MAGIC}, + {"coda", LINUX_CODA_SUPER_MAGIC}, + {"devfs", LINUX_DEVFS_SUPER_MAGIC}, + {"tmpfs", LINUX_SHMFS_MAGIC}, + {"linsysfs", LINUX_SYSFS_MAGIC}, + {NULL, 0L}}; for (i = 0; b2l_tbl[i].bsd_name != NULL; i++) if (strcmp(b2l_tbl[i].bsd_name, fstypename) == 0) diff --git a/sys/compat/linuxkpi/common/include/asm/pgtable.h b/sys/compat/linuxkpi/common/include/asm/pgtable.h index 865662d587db..166d6142d51c 100644 --- a/sys/compat/linuxkpi/common/include/asm/pgtable.h +++ b/sys/compat/linuxkpi/common/include/asm/pgtable.h @@ -53,6 +53,40 @@ typedef struct page *pgtable_t; #define _PAGE_PWT (((pteval_t) 1) << _PAGE_BIT_PWT) #define _PAGE_PCD (((pteval_t) 1) << _PAGE_BIT_PCD) #define _PAGE_PAT (((pteval_t) 1) << _PAGE_BIT_PAT) + +/* + * On Linux, the value of `PMD_SHIFT` is hard-coded to 21. This corresponds to + * the FreeBSD `PDRSHIFT` constant. + */ +#define PMD_SHIFT PDRSHIFT + +#elif defined(__aarch64__) + +/* + * On Linux, the value of `PMD_SHIFT` is computed from `CONFIG_PGTABLE_LEVELS`. + * The result corresponds to one of the FreeBSD `L*_SHIFT` constants. Here, we + * take the value 21 computed from `CONFIG_PGTABLE_LEVELS = 4`, the default on + * aarch64, which equals to `L2_SHIFT`. + */ +#define PMD_SHIFT L2_SHIFT + +#elif defined(__powerpc__) + +/* + * On Linux, the value of `PMD_SHIFT` is the addition of `PAGE_SHIFT` and + * `PTE_INDEX_SIZE` (hard-coded to 9). The result corresponds to the FreeBSD + * `L3_PAGE_SIZE_SHIFT` constant. + */ +#define PMD_SHIFT L3_PAGE_SIZE_SHIFT + +#elif defined(__riscv) + +/* + * On Linux, the value of `PMD_SHIFT` is hard-coded to 21. This corresponds to + * the FreeBSD `L2_SHIFT` constant. + */ +#define PMD_SHIFT L2_SHIFT + #endif #endif /* _LINUXKPI_ASM_PGTABLE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/ascii85.h b/sys/compat/linuxkpi/common/include/linux/ascii85.h new file mode 100644 index 000000000000..06777a130e41 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/ascii85.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 The FreeBSD Foundation + */ + +#ifndef _LINUXKPI_LINUX_ASCII85_H_ +#define _LINUXKPI_LINUX_ASCII85_H_ + +#include <sys/param.h> + +#define ASCII85_BUFSZ 6 + +static inline long +ascii85_encode_len(long in_len) +{ + long out_len; + + out_len = howmany(in_len, 4); + + return (out_len); +} + +static inline const char * +ascii85_encode(uint32_t in, char *out) +{ + int i; + + if (in == 0) { + out[0] = 'z'; + out[1] = '\0'; + return (out); + } + + for (i = ASCII85_BUFSZ - 2; i >= 0; i--) { + out[i] = in % 85; + out[i] += 33; + + in /= 85; + } + out[ASCII85_BUFSZ - 1] = '\0'; + + return (out); +} + +#endif /* _LINUXKPI_LINUX_ASCII85_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/compiler_types.h b/sys/compat/linuxkpi/common/include/linux/compiler_types.h index 7151c03de690..25e69f5afd8e 100644 --- a/sys/compat/linuxkpi/common/include/linux/compiler_types.h +++ b/sys/compat/linuxkpi/common/include/linux/compiler_types.h @@ -42,4 +42,17 @@ #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) +/* + * __builtin_counted_by_ref was introduced to + * - gcc in e7380688fa59 with a first release tag of gcc-15.1.0, + * - llvm in 7475156d49406 with a first release tag of llvmorg-20.1.0-rc1 + * but cannot be used before 23 (22.x.y possibly) (see 09a3d830a888). + */ +#if (__has_builtin(__builtin_counted_by_ref)) && \ + (!defined(__clang__) || (__clang_major__ >= 23)) +#define __flex_counter(_field) __builtin_counted_by_ref(_field) +#else +#define __flex_counter(_field) ((void *)NULL) +#endif + #endif diff --git a/sys/compat/linuxkpi/common/include/linux/device.h b/sys/compat/linuxkpi/common/include/linux/device.h index 8834f1799e61..030e9b127540 100644 --- a/sys/compat/linuxkpi/common/include/linux/device.h +++ b/sys/compat/linuxkpi/common/include/linux/device.h @@ -701,6 +701,13 @@ devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp) return (dst); } +static inline void * +devm_kmemdup_array(struct device *dev, const void *src, size_t n, size_t len, + gfp_t gfp) +{ + return (devm_kmemdup(dev, src, size_mul(n, len), gfp)); +} + #define devm_kzalloc(_dev, _size, _gfp) \ devm_kmalloc((_dev), (_size), (_gfp) | __GFP_ZERO) diff --git a/sys/compat/linuxkpi/common/include/linux/gfp.h b/sys/compat/linuxkpi/common/include/linux/gfp.h index 7a32e7862338..af7cdb422fcb 100644 --- a/sys/compat/linuxkpi/common/include/linux/gfp.h +++ b/sys/compat/linuxkpi/common/include/linux/gfp.h @@ -80,6 +80,9 @@ CTASSERT((__GFP_DMA32 & GFP_NATIVE_MASK) == 0); CTASSERT((__GFP_BITS_MASK & GFP_NATIVE_MASK) == GFP_NATIVE_MASK); +#define __default_gfp(_discard, _arg_or_default, ...) _arg_or_default +#define default_gfp(...) __default_gfp(, ##__VA_ARGS__, GFP_KERNEL) + struct page_frag_cache { void *va; int pagecnt_bias; diff --git a/sys/compat/linuxkpi/common/include/linux/hex.h b/sys/compat/linuxkpi/common/include/linux/hex.h new file mode 100644 index 000000000000..7e9f499cfb1b --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/hex.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022-2026 Bjoern A. Zeeb + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _LINUXKPI_LINUX_HEX_H_ +#define _LINUXKPI_LINUX_HEX_H_ + +#include <linux/types.h> +#include <linux/errno.h> + +static inline int +_h2b(const char c) +{ + + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (10 + c - 'a'); + if (c >= 'A' && c <= 'F') + return (10 + c - 'A'); + return (-EINVAL); +} + +static inline int +hex2bin(uint8_t *bindst, const char *hexsrc, size_t binlen) +{ + int hi4, lo4; + + while (binlen > 0) { + hi4 = _h2b(*hexsrc++); + lo4 = _h2b(*hexsrc++); + if (hi4 < 0 || lo4 < 0) + return (-EINVAL); + + *bindst++ = (hi4 << 4) | lo4; + binlen--; + } + + return (0); +} + +#endif /* _LINUXKPI_LINUX_HEX_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h b/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h new file mode 100644 index 000000000000..200a8c3f1d5e --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/instruction_pointer.h @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Neel Chauhan + * Copyright (c) 2026 The FreeBSD Foundation + */ + +#ifndef _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_ +#define _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_ + +#define _RET_IP_ __builtin_return_address(0) + +#define _THIS_IP_ ((unsigned long)0) /* TODO: _THIS_IP_ not implemented. */ + +#endif /* _LINUXKPI_LINUX_INSTRUCTION_POINTER_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/ioport.h b/sys/compat/linuxkpi/common/include/linux/ioport.h index 763af2de7c4f..68f28cec4ec4 100644 --- a/sys/compat/linuxkpi/common/include/linux/ioport.h +++ b/sys/compat/linuxkpi/common/include/linux/ioport.h @@ -41,6 +41,7 @@ struct resource { resource_size_t start; resource_size_t end; const char *name; + unsigned long flags; }; static inline resource_size_t diff --git a/sys/compat/linuxkpi/common/include/linux/kconfig.h b/sys/compat/linuxkpi/common/include/linux/kconfig.h index c1d186b56e1f..529d41991900 100644 --- a/sys/compat/linuxkpi/common/include/linux/kconfig.h +++ b/sys/compat/linuxkpi/common/include/linux/kconfig.h @@ -73,4 +73,39 @@ #define IS_REACHABLE(_x) (IS_BUILTIN(_x) || \ (IS_MODULE(_x) && IS_BUILTIN(MODULE))) +/* + * On Linux, the CONFIG_PGTABLE_LEVELS value is defined by the config/build + * system. Here, we take the per-architecture default value defined in + * `arch/$ARCH/Kconfig` files upstream. + */ +#if defined(__amd64__) + +#define CONFIG_PGTABLE_LEVELS 4 + +#elif defined(__aarch64__) + +#define CONFIG_PGTABLE_LEVELS 4 + +#elif defined(__i386__) + +#define CONFIG_PGTABLE_LEVELS 2 + +#elif defined(__powerpc__) + +#if defined(__powerpc64__) +#define CONFIG_PGTABLE_LEVELS 4 +#else +#define CONFIG_PGTABLE_LEVELS 2 +#endif + +#elif defined(__riscv) + +#if defined(__riscv_xlen) && __riscv_xlen == 64 +#define CONFIG_PGTABLE_LEVELS 5 +#else +#define CONFIG_PGTABLE_LEVELS 2 +#endif + +#endif + #endif /* _LINUXKPI_LINUX_KCONFIG_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/kernel.h b/sys/compat/linuxkpi/common/include/linux/kernel.h index 11a13cbd49b4..2430b25c6915 100644 --- a/sys/compat/linuxkpi/common/include/linux/kernel.h +++ b/sys/compat/linuxkpi/common/include/linux/kernel.h @@ -55,6 +55,8 @@ #include <linux/jiffies.h> #include <linux/log2.h> #include <linux/kconfig.h> +#include <linux/instruction_pointer.h> +#include <linux/hex.h> #include <asm/byteorder.h> #include <asm/cpufeature.h> @@ -267,8 +269,6 @@ extern int linuxkpi_debug; #define u64_to_user_ptr(val) ((void *)(uintptr_t)(val)) -#define _RET_IP_ __builtin_return_address(0) - #define offsetofend(t, m) \ (offsetof(t, m) + sizeof((((t *)0)->m))) @@ -306,37 +306,6 @@ linux_ratelimited(linux_ratelimit_t *rl) #define add_taint(x,y) do { \ } while (0) -static inline int -_h2b(const char c) -{ - - if (c >= '0' && c <= '9') - return (c - '0'); - if (c >= 'a' && c <= 'f') - return (10 + c - 'a'); - if (c >= 'A' && c <= 'F') - return (10 + c - 'A'); - return (-EINVAL); -} - -static inline int -hex2bin(uint8_t *bindst, const char *hexsrc, size_t binlen) -{ - int hi4, lo4; - - while (binlen > 0) { - hi4 = _h2b(*hexsrc++); - lo4 = _h2b(*hexsrc++); - if (hi4 < 0 || lo4 < 0) - return (-EINVAL); - - *bindst++ = (hi4 << 4) | lo4; - binlen--; - } - - return (0); -} - static inline bool mac_pton(const char *macin, uint8_t *macout) { diff --git a/sys/compat/linuxkpi/common/include/linux/kfifo.h b/sys/compat/linuxkpi/common/include/linux/kfifo.h index fbe16e22683e..b0d0c17f07e4 100644 --- a/sys/compat/linuxkpi/common/include/linux/kfifo.h +++ b/sys/compat/linuxkpi/common/include/linux/kfifo.h @@ -89,7 +89,7 @@ (_kf)->head[(_kf)->last] = (_e); \ (_kf)->count++; \ (_kf)->last++; \ - if ((_kf)->last > (_kf)->total) \ + if ((_kf)->last >= (_kf)->total) \ (_kf)->last = 0; \ _rc = true; \ } \ @@ -107,7 +107,7 @@ *(_e) = (_kf)->head[(_kf)->first]; \ (_kf)->count--; \ (_kf)->first++; \ - if ((_kf)->first > (_kf)->total) \ + if ((_kf)->first >= (_kf)->total) \ (_kf)->first = 0; \ _rc = true; \ } \ diff --git a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h index 25f96b304f59..539981c54d1b 100644 --- a/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h +++ b/sys/compat/linuxkpi/common/include/linux/kmsg_dump.h @@ -25,6 +25,11 @@ enum kmsg_dump_reason { KMSG_DUMP_MAX }; +struct kmsg_dump_iter { + uint64_t cur_seq; + uint64_t next_seq; +}; + struct kmsg_dumper { struct list_head list; void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason); @@ -32,6 +37,36 @@ struct kmsg_dumper { bool registered; }; +static inline void +kmsg_dump(enum kmsg_dump_reason reason) +{ + pr_debug("TODO"); +} + +static inline bool +kmsg_dump_get_line(struct kmsg_dump_iter *iter, bool syslog, + const char *line, size_t size, size_t *len) +{ + pr_debug("TODO"); + + return (false); +} + +static inline bool +kmsg_dump_get_buffer(struct kmsg_dump_iter *iter, bool syslog, + char *buf, size_t size, size_t *len) +{ + pr_debug("TODO"); + + return (false); +} + +static inline void +kmsg_dump_rewind(struct kmsg_dump_iter *iter) +{ + pr_debug("TODO"); +} + static inline int kmsg_dump_register(struct kmsg_dumper *dumper) { @@ -48,4 +83,11 @@ kmsg_dump_unregister(struct kmsg_dumper *dumper) return (-EINVAL); } +static inline const char * +kmsg_dump_reason_str(enum kmsg_dump_reason reason) +{ + pr_debug("TODO"); + + return ("Unknown"); +} #endif diff --git a/sys/compat/linuxkpi/common/include/linux/linux_logo.h b/sys/compat/linuxkpi/common/include/linux/linux_logo.h new file mode 100644 index 000000000000..cb60ba50f6a5 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/linux_logo.h @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 The FreeBSD Foundation + */ + +#ifndef _LINUXKPI_LINUX_LINUX_LOGO_H_ +#define _LINUXKPI_LINUX_LINUX_LOGO_H_ + +struct linux_logo { + int type; + unsigned int width; + unsigned int height; + unsigned int clutsize; + const unsigned char *clut; + const unsigned char *data; +}; + +#endif /* _LINUXKPI_LINUX_LINUX_LOGO_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/mfd/core.h b/sys/compat/linuxkpi/common/include/linux/mfd/core.h new file mode 100644 index 000000000000..1a69a3803b5d --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/mfd/core.h @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 The FreeBSD Foundation + */ + +#ifndef _LINUXKPI_LINUX_MFD_CORE_H_ +#define _LINUXKPI_LINUX_MFD_CORE_H_ + +#include <linux/platform_device.h> + +/* + * <linux/ioport.h> is not included by Linux, but we need it here to get the + * definition of `struct resource`. + * + * At least the amdgpu DRM driver (amdgpu_isp.c at the time of this writing) + * needs the structure without including this header: it relies on an implicit + * include of <linux/ioport.h> from <linux/pci.h>, which we can't have due to + * conflict with the FreeBSD native `struct resource`. + */ +#include <linux/ioport.h> + +#include <linux/kernel.h> /* pr_debug */ + +struct resource; +struct mfd_cell { + const char *name; + void *platform_data; + size_t pdata_size; + int num_resources; + const struct resource *resources; +}; + +static inline int +mfd_add_hotplug_devices(struct device *parent, + const struct mfd_cell *cells, int n_devs) +{ + pr_debug("%s: TODO\n", __func__); + + return (0); +} + +static inline void +mfd_remove_devices(struct device *parent) +{ + pr_debug("%s: TODO\n", __func__); +} + +#endif /* _LINUXKPI_LINUX_MFD_CORE_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/minmax.h b/sys/compat/linuxkpi/common/include/linux/minmax.h index fb8eb6f704b4..5040d7f9141e 100644 --- a/sys/compat/linuxkpi/common/include/linux/minmax.h +++ b/sys/compat/linuxkpi/common/include/linux/minmax.h @@ -60,6 +60,9 @@ type __max2 = (y); \ __max1 > __max2 ? __max1 : __max2; }) +#define MIN_T(type, x, y) MIN((type)(x), (type)(y)) +#define MAX_T(type, x, y) MAX((type)(x), (type)(y)) + #define clamp_t(type, _x, min, max) min_t(type, max_t(type, _x, min), max) #define clamp(x, lo, hi) min(max(x, lo), hi) #define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi) diff --git a/sys/compat/linuxkpi/common/include/linux/overflow.h b/sys/compat/linuxkpi/common/include/linux/overflow.h index e811037b8ecc..4326f05e6d07 100644 --- a/sys/compat/linuxkpi/common/include/linux/overflow.h +++ b/sys/compat/linuxkpi/common/include/linux/overflow.h @@ -4,9 +4,7 @@ #include <linux/compiler.h> #include <linux/limits.h> -#ifdef __linux__ #include <linux/const.h> -#endif /* * We need to compute the minimum and maximum values representable in a given @@ -38,19 +36,13 @@ #define __type_min(T) ((T)((T)-type_max(T)-(T)1)) #define type_min(t) __type_min(typeof(t)) -/* - * Avoids triggering -Wtype-limits compilation warning, - * while using unsigned data types to check a < 0. - */ -#define is_non_negative(a) ((a) > 0 || (a) == 0) -#define is_negative(a) (!(is_non_negative(a))) /* * Allows for effectively applying __must_check to a macro so we can have * both the type-agnostic benefits of the macros while also being able to * enforce that the return value is, in fact, checked. */ -static inline bool __must_check __must_check_overflow(bool overflow) +static __always_inline bool __must_check __must_check_overflow(bool overflow) { return unlikely(overflow); } @@ -203,9 +195,9 @@ static inline bool __must_check __must_check_overflow(bool overflow) typeof(d) _d = d; \ unsigned long long _a_full = _a; \ unsigned int _to_shift = \ - is_non_negative(_s) && _s < 8 * sizeof(*d) ? _s : 0; \ + _s >= 0 && _s < 8 * sizeof(*d) ? _s : 0; \ *_d = (_a_full << _to_shift); \ - (_to_shift != _s || is_negative(*_d) || is_negative(_a) || \ + (_to_shift != _s || *_d < 0 || _a < 0 || \ (*_d >> _to_shift) != _a); \ })) @@ -241,6 +233,76 @@ static inline bool __must_check __must_check_overflow(bool overflow) __overflows_type(n, T)) /** + * range_overflows() - Check if a range is out of bounds + * @start: Start of the range. + * @size: Size of the range. + * @max: Exclusive upper boundary. + * + * A strict check to determine if the range [@start, @start + @size) is + * invalid with respect to the allowable range [0, @max). Any range + * starting at or beyond @max is considered an overflow, even if @size is 0. + * + * Returns: true if the range is out of bounds. + */ +#define range_overflows(start, size, max) ({ \ + typeof(start) start__ = (start); \ + typeof(size) size__ = (size); \ + typeof(max) max__ = (max); \ + (void)(&start__ == &size__); \ + (void)(&start__ == &max__); \ + start__ >= max__ || size__ > max__ - start__; \ +}) + +/** + * range_overflows_t() - Check if a range is out of bounds + * @type: Data type to use. + * @start: Start of the range. + * @size: Size of the range. + * @max: Exclusive upper boundary. + * + * Same as range_overflows() but forcing the parameters to @type. + * + * Returns: true if the range is out of bounds. + */ +#define range_overflows_t(type, start, size, max) \ + range_overflows((type)(start), (type)(size), (type)(max)) + +/** + * range_end_overflows() - Check if a range's endpoint is out of bounds + * @start: Start of the range. + * @size: Size of the range. + * @max: Exclusive upper boundary. + * + * Checks only if the endpoint of a range (@start + @size) exceeds @max. + * Unlike range_overflows(), a zero-sized range at the boundary (@start == @max) + * is not considered an overflow. Useful for iterator-style checks. + * + * Returns: true if the endpoint exceeds the boundary. + */ +#define range_end_overflows(start, size, max) ({ \ + typeof(start) start__ = (start); \ + typeof(size) size__ = (size); \ + typeof(max) max__ = (max); \ + (void)(&start__ == &size__); \ + (void)(&start__ == &max__); \ + start__ > max__ || size__ > max__ - start__; \ +}) + +/** + * range_end_overflows_t() - Check if a range's endpoint is out of bounds + * @type: Data type to use. + * @start: Start of the range. + * @size: Size of the range. + * @max: Exclusive upper boundary. + * + * Same as range_end_overflows() but forcing the parameters to @type. + * + * Returns: true if the endpoint exceeds the boundary. + */ +#define range_end_overflows_t(type, start, size, max) \ + range_end_overflows((type)(start), (type)(size), (type)(max)) + +/** * castable_to_type - like __same_type(), but also allows for casted literals * * @n: variable or constant value @@ -265,7 +327,7 @@ static inline bool __must_check __must_check_overflow(bool overflow) * with any overflow causing the return value to be SIZE_MAX. The * lvalue must be size_t to avoid implicit type conversion. */ -static inline size_t __must_check size_mul(size_t factor1, size_t factor2) +static __always_inline size_t __must_check size_mul(size_t factor1, size_t factor2) { size_t bytes; @@ -284,7 +346,7 @@ static inline size_t __must_check size_mul(size_t factor1, size_t factor2) * with any overflow causing the return value to be SIZE_MAX. The * lvalue must be size_t to avoid implicit type conversion. */ -static inline size_t __must_check size_add(size_t addend1, size_t addend2) +static __always_inline size_t __must_check size_add(size_t addend1, size_t addend2) { size_t bytes; @@ -305,7 +367,7 @@ static inline size_t __must_check size_add(size_t addend1, size_t addend2) * argument may be SIZE_MAX (or the result with be forced to SIZE_MAX). * The lvalue must be size_t to avoid implicit type conversion. */ -static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) +static __always_inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) { size_t bytes; @@ -391,6 +453,18 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) struct_size((type *)NULL, member, count) /** + * struct_offset() - Calculate the offset of a member within a struct + * @p: Pointer to the struct + * @member: Name of the member to get the offset of + * + * Calculates the offset of a particular @member of the structure pointed + * to by @p. + * + * Return: number of bytes to the location of @member. + */ +#define struct_offset(p, member) (offsetof(typeof(*(p)), member)) + +/** * __DEFINE_FLEX() - helper macro for DEFINE_FLEX() family. * Enables caller macro to pass arbitrary trailing expressions * @@ -472,4 +546,46 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) (__member_size((name)->array) / sizeof(*(name)->array) + \ __must_be_array((name)->array)) +/** + * typeof_flex_counter() - Return the type of the counter variable of a given + * flexible array member annotated by __counted_by(). + * @FAM: Instance of flexible array member within a given struct. + * + * Returns: "size_t" if no annotation exists. + */ +#define typeof_flex_counter(FAM) \ + typeof(_Generic(__flex_counter(FAM), \ + void *: (size_t)0, \ + default: *__flex_counter(FAM))) + +/** + * overflows_flex_counter_type() - Check if the counter associated with the + * given flexible array member can represent + * a value. + * @TYPE: Type of the struct that contains the @FAM. + * @FAM: Member name of the FAM within @TYPE. + * @COUNT: Value to check against the __counted_by annotated @FAM's counter. + * + * Returns: true if @COUNT can be represented in the @FAM's counter. When + * @FAM is not annotated with __counted_by(), always returns true. + */ +#define overflows_flex_counter_type(TYPE, FAM, COUNT) \ + (overflows_type(COUNT, typeof_flex_counter(((TYPE *)NULL)->FAM))) + +/** + * __set_flex_counter() - Set the counter associated with the given flexible + * array member that has been annoated by __counted_by(). + * @FAM: Instance of flexible array member within a given struct. + * @COUNT: Value to store to the __counted_by annotated @FAM_PTR's counter. + * + * This is a no-op if no annotation exists. Count needs to be checked with + * overflows_flex_counter_type() before using this function. + */ +#define __set_flex_counter(FAM, COUNT) \ +({ \ + *_Generic(__flex_counter(FAM), \ + void *: &(size_t){ 0 }, \ + default: __flex_counter(FAM)) = (COUNT); \ +}) + #endif /* _LINUXKPI_LINUX_OVERFLOW_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h index ccbd425de5da..ba68a9ee0dc3 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -60,6 +60,17 @@ #include <linux/pci_ids.h> #include <linux/pm.h> +/* + * <linux/ioport.h> should be included here, like Linux, but we can't have that + * because Linux `struct resource` definition would conflict with FreeBSD + * native definition. + * + * At least the amdgpu DRM driver (amdgpu_isp.c at the time of this writing) + * relies on this indirect include to get the definition of Linux `struct + * resource`. As a workaround, we include <linux/ioport.h> from + * <linux/mfd/core.h>. + */ + #include <linux/kernel.h> /* pr_debug */ struct pci_device_id { diff --git a/sys/compat/linuxkpi/common/include/linux/sizes.h b/sys/compat/linuxkpi/common/include/linux/sizes.h index d8a6e75192f6..430ad3ec427d 100644 --- a/sys/compat/linuxkpi/common/include/linux/sizes.h +++ b/sys/compat/linuxkpi/common/include/linux/sizes.h @@ -29,6 +29,17 @@ #ifndef _LINUXKPI_LINUX_SIZES_H_ #define _LINUXKPI_LINUX_SIZES_H_ +#define SZ_1 1 +#define SZ_2 2 +#define SZ_4 4 +#define SZ_8 8 +#define SZ_16 16 +#define SZ_32 32 +#define SZ_64 64 +#define SZ_128 128 +#define SZ_256 256 +#define SZ_512 512 + #define SZ_1K (1024 * 1) #define SZ_2K (1024 * 2) #define SZ_4K (1024 * 4) diff --git a/sys/compat/linuxkpi/common/include/linux/slab.h b/sys/compat/linuxkpi/common/include/linux/slab.h index 0e649e1e3c4a..6c05c77819a5 100644 --- a/sys/compat/linuxkpi/common/include/linux/slab.h +++ b/sys/compat/linuxkpi/common/include/linux/slab.h @@ -4,7 +4,7 @@ * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2021 Mellanox Technologies, Ltd. * All rights reserved. - * Copyright (c) 2024-2025 The FreeBSD Foundation + * Copyright (c) 2024-2026 The FreeBSD Foundation * * Portions of this software were developed by Björn Zeeb * under sponsorship from the FreeBSD Foundation. @@ -51,6 +51,22 @@ MALLOC_DECLARE(M_KMALLOC); #define kvcalloc(n, size, flags) kvmalloc_array(n, size, (flags) | __GFP_ZERO) #define kzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO) #define kzalloc_node(size, flags, node) kmalloc_node(size, (flags) | __GFP_ZERO, node) +#define kzalloc_obj(_p, ...) \ + kzalloc(sizeof(typeof(_p)), default_gfp(__VA_ARGS__)) +#define kzalloc_objs(_p, _n, ...) \ + kzalloc(size_mul((_n), sizeof(typeof(_p))), default_gfp(__VA_ARGS__)) +#define kzalloc_flex(_p, _field, _n, ...) \ +({ \ + const size_t __n = (_n); \ + const size_t __psize = struct_size_t(typeof(_p), _field, __n); \ + typeof(_p) *__p_obj; \ + \ + __p_obj = kzalloc(__psize, default_gfp(__VA_ARGS__)); \ + if (__p_obj != NULL) \ + __set_flex_counter(__p_obj->_field, __n); \ + \ + __p_obj; \ +}) #define kfree_const(ptr) kfree(ptr) #define kfree_async(ptr) kfree(ptr) /* drm-kmod 5.4 compat */ #define vzalloc(size) __vmalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, 0) @@ -143,6 +159,12 @@ kmalloc_node(size_t size, gfp_t flags, int node) return (lkpi___kmalloc_node(size, flags, node)); } +#define kmalloc_obj(_p, ...) \ + kmalloc(sizeof(typeof(_p)), default_gfp(__VA_ARGS__)) + +#define kmalloc_objs(_p, _n, ...) \ + kmalloc(size_mul((_n) * sizeof(typeof(_p))), default_gfp(__VA_ARGS__)) + static inline void * krealloc(void *ptr, size_t size, gfp_t flags) { diff --git a/sys/compat/linuxkpi/common/include/linux/string.h b/sys/compat/linuxkpi/common/include/linux/string.h index f7b64560d254..0b858e7af623 100644 --- a/sys/compat/linuxkpi/common/include/linux/string.h +++ b/sys/compat/linuxkpi/common/include/linux/string.h @@ -303,6 +303,22 @@ memcpy_and_pad(void *dst, size_t dstlen, const void *src, size_t len, int ch) } } +#define strtomem(dst, src) do { \ + size_t dstlen = ARRAY_SIZE(dst); \ + size_t srclen = __builtin_object_size(src, 1); \ + srclen = MIN(srclen, dstlen); \ + srclen = strnlen(src, srclen); \ + memcpy(dst, src, srclen); \ +} while (0) + +#define strtomem_pad(dst, src, pad) do { \ + size_t dstlen = ARRAY_SIZE(dst); \ + size_t srclen = __builtin_object_size(src, 1); \ + srclen = MIN(srclen, dstlen); \ + srclen = strnlen(src, srclen); \ + memcpy_and_pad(dst, dstlen, src, srclen, pad); \ +} while (0) + #define memset_startat(ptr, bytepat, smember) \ ({ \ uint8_t *_ptr = (uint8_t *)(ptr); \ diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h index 470c224a9778..7c8c4e2e32b9 100644 --- a/sys/compat/linuxkpi/common/include/linux/sysfs.h +++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h @@ -43,13 +43,6 @@ struct sysfs_ops { size_t); }; -struct attribute_group { - const char *name; - mode_t (*is_visible)(struct kobject *, - struct attribute *, int); - struct attribute **attrs; -}; - struct bin_attribute { struct attribute attr; size_t size; @@ -59,6 +52,14 @@ struct bin_attribute { struct bin_attribute *, char *, loff_t, size_t); }; +struct attribute_group { + const char *name; + mode_t (*is_visible)(struct kobject *, + struct attribute *, int); + struct attribute **attrs; + struct bin_attribute **bin_attrs; +}; + #define __ATTR(_name, _mode, _show, _store) { \ .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, .store = _store, \ @@ -370,6 +371,7 @@ static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; /* Don't create the group node if grp->name is undefined. */ @@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); else oidp = kobj->oidp; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, + (*bin_attr)->attr.name, + CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE, + kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", ""); + } return (0); } @@ -434,14 +444,20 @@ static inline void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) { if (strcmp(oidp->oid_name, grp->name) != 0) continue; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { sysctl_remove_name(oidp, (*attr)->name, 1, 1); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1); + } } } diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h index 4f3aad532810..c637e13a496d 100644 --- a/sys/compat/linuxkpi/common/include/net/mac80211.h +++ b/sys/compat/linuxkpi/common/include/net/mac80211.h @@ -1196,6 +1196,22 @@ void linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *, /* -------------------------------------------------------------------------- */ +/* + * Emulate chanctx operations. We cannot rename/prefix the functions + * as we rely on the (function)pointers being the same everywhere. + */ +int ieee80211_emulate_add_chanctx(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *); +void ieee80211_emulate_remove_chanctx(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *); +void ieee80211_emulate_change_chanctx(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *, uint32_t); +int ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *, + struct ieee80211_vif_chanctx_switch *, int, + enum ieee80211_chanctx_switch_mode); + +/* -------------------------------------------------------------------------- */ + static __inline void _ieee80211_hw_set(struct ieee80211_hw *hw, enum ieee80211_hw_flags flag) { @@ -2634,60 +2650,4 @@ ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp __unused) /* -------------------------------------------------------------------------- */ -int lkpi_80211_update_chandef(struct ieee80211_hw *, - struct ieee80211_chanctx_conf *); - -static inline int -ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *chanctx_conf) -{ - int error; - - hw->conf.radar_enabled = chanctx_conf->radar_enabled; - error = lkpi_80211_update_chandef(hw, chanctx_conf); - return (error); -} - -static inline void -ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *chanctx_conf __unused) -{ - hw->conf.radar_enabled = false; - lkpi_80211_update_chandef(hw, NULL); -} - -static inline void -ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed __unused) -{ - hw->conf.radar_enabled = chanctx_conf->radar_enabled; - lkpi_80211_update_chandef(hw, chanctx_conf); -} - -static inline int -ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif_chanctx_switch *vifs, int n_vifs, - enum ieee80211_chanctx_switch_mode mode __unused) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - int error; - - /* Sanity check. */ - if (n_vifs <= 0) - return (-EINVAL); - if (vifs == NULL || vifs[0].new_ctx == NULL) - return (-EINVAL); - - /* - * What to do if n_vifs > 1? - * Does that make sense for drivers not supporting chanctx? - */ - hw->conf.radar_enabled = vifs[0].new_ctx->radar_enabled; - chanctx_conf = vifs[0].new_ctx; - error = lkpi_80211_update_chandef(hw, chanctx_conf); - return (error); -} - -/* -------------------------------------------------------------------------- */ - #endif /* _LINUXKPI_NET_MAC80211_H */ diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index 01347586ef63..b9528295ad8e 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -181,6 +181,8 @@ static void lkpi_ieee80211_free_skb_mbuf(void *); #ifdef LKPI_80211_WME static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool); #endif +static int lkpi_80211_update_chandef(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *); static void lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *); static const char * @@ -766,6 +768,7 @@ lkpi_sta_sync_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif, lkpi_sync_chanctx_cw_from_rx_bw(hw, vif, sta); } +#if 0 static uint8_t lkpi_get_max_rx_chains(struct ieee80211_node *ni) { @@ -792,6 +795,7 @@ lkpi_get_max_rx_chains(struct ieee80211_node *ni) return (chains); } +#endif static void lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni, @@ -2009,12 +2013,23 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni, * make sure we do not do it on every beacon we still may * get so only do if something changed. vif->bss_conf.dtim_period * should be 0 as we start up (we also reset it on teardown). + * + * If we are assoc we need to make sure dtim_period is non-0. + * 0 is a reserved value and drivers assume they can DIV by it. + * In theory this means we need to wait for the first beacon + * before we finalize the vif being assoc. In practise that + * is harder until net80211 learns how to. Work around like + * this for the moment. */ - if (vif->cfg.assoc && - vif->bss_conf.dtim_period != ni->ni_dtim_period && - ni->ni_dtim_period > 0) { - vif->bss_conf.dtim_period = ni->ni_dtim_period; - bss_changed |= BSS_CHANGED_BEACON_INFO; + if (vif->cfg.assoc) { + if (vif->bss_conf.dtim_period != ni->ni_dtim_period && + ni->ni_dtim_period > 0) { + vif->bss_conf.dtim_period = ni->ni_dtim_period; + bss_changed |= BSS_CHANGED_BEACON_INFO; + } else if (vif->bss_conf.dtim_period == 0) { + vif->bss_conf.dtim_period = 1; + bss_changed |= BSS_CHANGED_BEACON_INFO; + } } vif->bss_conf.sync_dtim_count = ni->ni_dtim_count; @@ -2214,13 +2229,218 @@ lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta) } } +static void +lkpi_init_chandef(struct ieee80211com *ic __unused, + struct cfg80211_chan_def *chandef, + struct linuxkpi_ieee80211_channel *chan, struct ieee80211_channel *c, + bool can_ht) +{ + + cfg80211_chandef_create(chandef, chan, + (can_ht) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); + chandef->center_freq1 = ieee80211_get_channel_center_freq1(c); + chandef->center_freq2 = ieee80211_get_channel_center_freq2(c); + + IMPROVE("Check ht/vht_cap from band not just chan? See lkpi_sta_sync_from_ni..."); +#ifdef LKPI_80211_HT + if (IEEE80211_IS_CHAN_HT(c)) { + if (IEEE80211_IS_CHAN_HT40(c)) + chandef->width = NL80211_CHAN_WIDTH_40; + else + chandef->width = NL80211_CHAN_WIDTH_20; + } +#endif +#ifdef LKPI_80211_VHT + if (IEEE80211_IS_CHAN_VHT_5GHZ(c)) { + if (IEEE80211_IS_CHAN_VHT80P80(c)) + chandef->width = NL80211_CHAN_WIDTH_80P80; + else if (IEEE80211_IS_CHAN_VHT160(c)) + chandef->width = NL80211_CHAN_WIDTH_160; + else if (IEEE80211_IS_CHAN_VHT80(c)) + chandef->width = NL80211_CHAN_WIDTH_80; + } +#endif + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(ic, "%s:%d: chandef %p { chan %p { %u }, " + "width %d cfreq1 %u cfreq2 %u punctured %u }\n", + __func__, __LINE__, chandef, + chandef->chan, chandef->chan->center_freq, + chandef->width, + chandef->center_freq1, chandef->center_freq2, + chandef->punctured); +#endif +} + +static uint32_t +lkpi_init_chanctx_conf(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + uint32_t changed; + + lockdep_assert_wiphy(hw->wiphy); + + changed = 0; + + chanctx_conf->rx_chains_static = 1; + chanctx_conf->rx_chains_dynamic = 1; + changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS; + + if (chanctx_conf->radar_enabled != hw->conf.radar_enabled) { + chanctx_conf->radar_enabled = hw->conf.radar_enabled; + changed |= IEEE80211_CHANCTX_CHANGE_RADAR; + } + + chanctx_conf->def = *chandef; + changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + + /* One day we should figure this out; is for iwlwifi-only. */ + chanctx_conf->min_def = chanctx_conf->def; + changed |= IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; + + /* chanctx_conf->ap = */ + + return (changed); +} + +static struct lkpi_chanctx * +lkpi_alloc_lchanctx(struct ieee80211_hw *hw, struct lkpi_vif *lvif) +{ + struct lkpi_chanctx *lchanctx; + + lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size, + M_LKPI80211, M_WAITOK | M_ZERO); + lchanctx->lvif = lvif; + + return (lchanctx); +} + +static struct lkpi_chanctx * +lkpi_find_lchanctx_reserved(struct ieee80211_hw *hw, struct lkpi_vif *lvif) +{ + struct lkpi_hw *lhw; + struct lkpi_chanctx *lchanctx; + bool found; + + lhw = HW_TO_LHW(hw); + + found = false; + rcu_read_lock(); + list_for_each_entry_rcu(lchanctx, &lhw->lchanctx_list_reserved, entry) { + if (lchanctx->lvif == lvif) { + found = true; + break; + } + } + rcu_read_unlock(); + + if (!found) { + lchanctx = lkpi_alloc_lchanctx(hw, lvif); + list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved); + } + + return (lchanctx); +} + +static struct ieee80211_chanctx_conf * +lkpi_get_chanctx_conf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + + chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf, + lockdep_is_held(&hw->wiphy->mtx)); + if (chanctx_conf == NULL) { + struct lkpi_chanctx *lchanctx; + struct lkpi_vif *lvif; + + lvif = VIF_TO_LVIF(vif); + lchanctx = lkpi_find_lchanctx_reserved(hw, lvif); + KASSERT(lchanctx != NULL, ("%s: hw %p, vif %p no lchanctx\n", + __func__, hw, vif)); + list_del(&lchanctx->entry); + chanctx_conf = &lchanctx->chanctx_conf; + } + /* else { IMPROVE("diff changes for changed, working on live copy, rcu"); } */ + + return (chanctx_conf); +} + +static int +lkpi_set_chanctx_conf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *chanctx_conf, + uint32_t changed, bool changed_set) +{ + struct lkpi_hw *lhw; + struct lkpi_chanctx *lchanctx; + int error; + + if (vif->bss_conf.chanctx_conf == chanctx_conf) { + if (!changed_set) { + IMPROVE("OBSOLETE?"); + changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; + changed |= IEEE80211_CHANCTX_CHANGE_RADAR; + changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS; + changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + } + lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed); + + return (0); + } + + lhw = HW_TO_LHW(hw); + + /* The device is no longer idle. */ + IMPROVE("Once we do multi-vif, only do for 1st chanctx"); + lkpi_hw_conf_idle(hw, false); + + error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf); + if (error != 0 && error != EOPNOTSUPP) { + ic_printf(lhw->ic, "%s:%d: mo_add_chanctx " + "failed: %d\n", __func__, __LINE__, error); + return (error); + } + + vif->bss_conf.chanreq.oper.chan = chanctx_conf->def.chan; + vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width; + vif->bss_conf.chanreq.oper.center_freq1 = + chanctx_conf->def.center_freq1; + vif->bss_conf.chanreq.oper.center_freq2 = + chanctx_conf->def.center_freq2; + + lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); + list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list); + rcu_assign_pointer(vif->bss_conf.chanctx_conf, chanctx_conf); + + /* Assign vif chanctx. */ + if (error == 0) + error = lkpi_80211_mo_assign_vif_chanctx(hw, vif, + &vif->bss_conf, chanctx_conf); + if (error == EOPNOTSUPP) + error = 0; + if (error != 0) { + ic_printf(lhw->ic, "%s:%d: mo_assign_vif_chanctx " + "failed: %d\n", __func__, __LINE__, error); + lkpi_80211_mo_remove_chanctx(hw, chanctx_conf); + rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL); + lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); + list_del(&lchanctx->entry); + list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved); + } + + return (error); +} static void lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct lkpi_hw *lhw; struct ieee80211_chanctx_conf *chanctx_conf; struct lkpi_chanctx *lchanctx; + lockdep_assert_wiphy(hw->wiphy); + chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf, lockdep_is_held(&hw->wiphy->mtx)); @@ -2239,7 +2459,8 @@ lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif) rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL); lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); list_del(&lchanctx->entry); - free(lchanctx, M_LKPI80211); + lhw = HW_TO_LHW(hw); + list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved); } /* -------------------------------------------------------------------------- */ @@ -2310,7 +2531,7 @@ static int lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct linuxkpi_ieee80211_channel *chan; - struct lkpi_chanctx *lchanctx; + struct cfg80211_chan_def chandef; struct ieee80211_chanctx_conf *chanctx_conf; struct lkpi_hw *lhw; struct ieee80211_hw *hw; @@ -2322,7 +2543,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int struct ieee80211_prep_tx_info prep_tx_info; uint32_t changed; int error; - bool synched; + bool synched, can_ht; /* * In here we use vap->iv_bss until lvif->lvif_bss is set. @@ -2379,65 +2600,35 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int wiphy_lock(hw->wiphy); /* Add chanctx (or if exists, change it). */ - chanctx_conf = rcu_dereference_protected(vif->bss_conf.chanctx_conf, - lockdep_is_held(&hw->wiphy->mtx)); - if (chanctx_conf != NULL) { - lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); - IMPROVE("diff changes for changed, working on live copy, rcu"); - } else { - /* Keep separate alloc as in Linux this is rcu managed? */ - lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size, - M_LKPI80211, M_WAITOK | M_ZERO); - chanctx_conf = &lchanctx->chanctx_conf; - } + chanctx_conf = lkpi_get_chanctx_conf(hw, vif); - chanctx_conf->rx_chains_static = 1; - chanctx_conf->rx_chains_dynamic = 1; - chanctx_conf->radar_enabled = - (chan->flags & IEEE80211_CHAN_RADAR) ? true : false; - chanctx_conf->def.chan = chan; - chanctx_conf->def.width = NL80211_CHAN_WIDTH_20_NOHT; - chanctx_conf->def.center_freq1 = ieee80211_get_channel_center_freq1(ni->ni_chan); - chanctx_conf->def.center_freq2 = ieee80211_get_channel_center_freq2(ni->ni_chan); - IMPROVE("Check vht_cap from band not just chan?"); KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC, ("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan)); #ifdef LKPI_80211_HT - if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { - if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) - chanctx_conf->def.width = NL80211_CHAN_WIDTH_40; - else - chanctx_conf->def.width = NL80211_CHAN_WIDTH_20; - } + can_ht = (vap->iv_ic->ic_flags_ht & IEEE80211_FHT_HT) != 0; +#else + can_ht = false; #endif -#ifdef LKPI_80211_VHT - if (IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) { - if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) - chanctx_conf->def.width = NL80211_CHAN_WIDTH_80P80; - else if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan)) - chanctx_conf->def.width = NL80211_CHAN_WIDTH_160; - else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) - chanctx_conf->def.width = NL80211_CHAN_WIDTH_80; - } + lkpi_init_chandef(vap->iv_ic, &chandef, chan, ni->ni_chan, can_ht); + hw->conf.radar_enabled = + ((chan->flags & IEEE80211_CHAN_RADAR) != 0) ? true : false; + hw->conf.chandef = chandef; + vif->bss_conf.chanreq.oper = hw->conf.chandef; +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(vap->iv_ic, "%s:%d: hw->conf.chandef %p = chandef %p = " + "vif->bss_conf.chanreq.oper %p\n", __func__, __LINE__, + &hw->conf.chandef, &chandef, &vif->bss_conf.chanreq.oper); #endif - chanctx_conf->rx_chains_dynamic = lkpi_get_max_rx_chains(ni); + + changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf); + /* Responder ... */ -#if 0 - chanctx_conf->min_def.chan = chanctx_conf->def.chan; - chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT; -#ifdef LKPI_80211_HT - if (IEEE80211_IS_CHAN_HT(ni->ni_chan) || IEEE80211_IS_CHAN_VHT(ni->ni_chan)) - chanctx_conf->min_def.width = NL80211_CHAN_WIDTH_20; -#endif - chanctx_conf->min_def.center_freq1 = chanctx_conf->def.center_freq1; - chanctx_conf->min_def.center_freq2 = chanctx_conf->def.center_freq2; -#else - chanctx_conf->min_def = chanctx_conf->def; -#endif /* Set bss info (bss_info_changed). */ bss_changed = 0; + IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ni->ni_bssid); vif->bss_conf.bssid = ni->ni_bssid; bss_changed |= BSS_CHANGED_BSSID; vif->bss_conf.txpower = ni->ni_txpower; @@ -2454,52 +2645,10 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__); - error = 0; - if (vif->bss_conf.chanctx_conf == chanctx_conf) { - changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; - changed |= IEEE80211_CHANCTX_CHANGE_RADAR; - changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS; - changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; - lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed); - } else { - /* The device is no longer idle. */ - IMPROVE("Once we do multi-vif, only do for 1st chanctx"); - lkpi_hw_conf_idle(hw, false); - - error = lkpi_80211_mo_add_chanctx(hw, chanctx_conf); - if (error == 0 || error == EOPNOTSUPP) { - vif->bss_conf.chanreq.oper.chan = chanctx_conf->def.chan; - vif->bss_conf.chanreq.oper.width = chanctx_conf->def.width; - vif->bss_conf.chanreq.oper.center_freq1 = - chanctx_conf->def.center_freq1; - vif->bss_conf.chanreq.oper.center_freq2 = - chanctx_conf->def.center_freq2; - } else { - ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx " - "failed: %d\n", __func__, __LINE__, error); - goto out; - } - - list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list); - rcu_assign_pointer(vif->bss_conf.chanctx_conf, chanctx_conf); + error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true); + if (error != 0) + goto out; - /* Assign vif chanctx. */ - if (error == 0) - error = lkpi_80211_mo_assign_vif_chanctx(hw, vif, - &vif->bss_conf, chanctx_conf); - if (error == EOPNOTSUPP) - error = 0; - if (error != 0) { - ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx " - "failed: %d\n", __func__, __LINE__, error); - lkpi_80211_mo_remove_chanctx(hw, chanctx_conf); - rcu_assign_pointer(vif->bss_conf.chanctx_conf, NULL); - lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); - list_del(&lchanctx->entry); - free(lchanctx, M_LKPI80211); - goto out; - } - } IMPROVE("update radiotap chan fields too"); /* RATES */ @@ -3330,6 +3479,7 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int bss_changed |= BSS_CHANGED_QOS; vif->cfg.ssid_len = 0; memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid)); + IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ieee80211broadcastaddr); bss_changed |= BSS_CHANGED_BSSID; vif->bss_conf.use_short_preamble = false; /* XXX BSS_CHANGED_???? */ @@ -3915,6 +4065,10 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], /* Need to fill in other fields as well. */ IMPROVE(); + /* Create a chanctx to be used later. */ + IMPROVE("lkpi_alloc_lchanctx reserved as many as can be"); + (void) lkpi_find_lchanctx_reserved(hw, lvif); + /* XXX-BZ hardcoded for now! */ #if 1 RCU_INIT_POINTER(vif->bss_conf.chanctx_conf, NULL); @@ -3924,12 +4078,14 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], lvif->lvif_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, lkpi_vif_iflladdr, vif, EVENTHANDLER_PRI_ANY); vif->bss_conf.link_id = 0; /* Non-MLO operation. */ + vif->bss_conf.chanreq.oper.chan = lhw->dflt_chandef.chan; vif->bss_conf.chanreq.oper.width = NL80211_CHAN_WIDTH_20_NOHT; vif->bss_conf.use_short_preamble = false; /* vap->iv_flags IEEE80211_F_SHPREAMBLE */ vif->bss_conf.use_short_slot = false; /* vap->iv_flags IEEE80211_F_SHSLOT */ vif->bss_conf.qos = false; vif->bss_conf.use_cts_prot = false; /* vap->iv_protmode */ vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + IEEE80211_ADDR_COPY(vif->cfg.ap_addr, ieee80211broadcastaddr); vif->cfg.aid = 0; vif->cfg.assoc = false; vif->cfg.idle = true; @@ -4070,7 +4226,6 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], if (hw->max_listen_interval == 0) hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval); hw->conf.listen_interval = hw->max_listen_interval; - ic->ic_set_channel(ic); /* XXX-BZ do we need to be able to update these? */ hw->wiphy->frag_threshold = vap->iv_fragthreshold; @@ -4952,6 +5107,9 @@ lkpi_ic_scan_end(struct ieee80211com *ic) */ lkpi_enable_hw_scan(lhw); + /* Clear the scanning chandef. */ + memset(&lhw->scan_chandef, 0, sizeof(lhw->scan_chandef)); + LKPI_80211_LHW_SCAN_LOCK(lhw); wakeup(lhw); LKPI_80211_LHW_SCAN_UNLOCK(lhw); @@ -4994,6 +5152,25 @@ lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss) lhw->ic_scan_mindwell(ss); } +struct lkpi_ic_set_channel_iter_arg { + struct linuxkpi_ieee80211_channel *chan; + struct ieee80211_chanctx_conf *chanctx_conf; +}; + +static void +lkpi_ic_set_channel_chanctx_iterf(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, void *arg) +{ + struct lkpi_ic_set_channel_iter_arg *chanctx_iter_arg; + + chanctx_iter_arg = arg; + if (chanctx_iter_arg->chanctx_conf != NULL) + return; + + if (chanctx_iter_arg->chan == chanctx_conf->def.chan) + chanctx_iter_arg->chanctx_conf = chanctx_conf; +} + static void lkpi_ic_set_channel(struct ieee80211com *ic) { @@ -5001,64 +5178,149 @@ lkpi_ic_set_channel(struct ieee80211com *ic) struct ieee80211_hw *hw; struct ieee80211_channel *c; struct linuxkpi_ieee80211_channel *chan; + struct ieee80211_chanctx_conf *chanctx_conf; + uint32_t changed; int error; - bool hw_scan_running; + bool hw_scan, scan_running; - lhw = ic->ic_softc; - - /* If we do not support (*config)() save us the work. */ - if (lhw->ops->config == NULL) - return; + IEEE80211_UNLOCK_ASSERT(ic); - /* If we have a hw_scan running do not switch channels. */ - LKPI_80211_LHW_SCAN_LOCK(lhw); - hw_scan_running = - (lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) == - (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW); - LKPI_80211_LHW_SCAN_UNLOCK(lhw); - if (hw_scan_running) - return; + lhw = ic->ic_softc; c = ic->ic_curchan; if (c == NULL || c == IEEE80211_CHAN_ANYC) { - ic_printf(ic, "%s: c %p ops->config %p\n", __func__, - c, lhw->ops->config); + ic_printf(ic, "%s: Unset channel: c %p, ignoring update\n", + __func__, c); return; } chan = lkpi_find_lkpi80211_chan(lhw, c); if (chan == NULL) { - ic_printf(ic, "%s: c %p chan %p\n", __func__, - c, chan); + ic_printf(ic, "%s: No channel found for c %p(%d) chan %p\n", + __func__, c, c->ic_ieee, chan); return; } - /* XXX max power for scanning? */ - IMPROVE(); + /* + * All net80211 callers call ieee80211_radiotap_chan_change(). + * That means we have nothing to do ourselves. + */ + + /* If we have a hw_scan running do not switch channels. */ + LKPI_80211_LHW_SCAN_LOCK(lhw); + scan_running = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0; + hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0; + LKPI_80211_LHW_SCAN_UNLOCK(lhw); + if (scan_running && hw_scan) { + TRACE_SCAN(ic, "scan_flags %b chan %d nothing to do.", + lhw->scan_flags, LKPI_LHW_SCAN_BITS, + c->ic_ieee); + /* Let us hope we set tx power levels elsewhere. */ + return; + } hw = LHW_TO_HW(lhw); - cfg80211_chandef_create(&hw->conf.chandef, chan, -#ifdef LKPI_80211_HT - (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 : + wiphy_lock(hw->wiphy); + if (scan_running) { + struct ieee80211vap *vap; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + + /* + * For now and for scanning just pick the first VIF. + * net80211 will need to grow DBDC/link_id support + * for us to find the vif/chanctx otherwise. + */ + vap = TAILQ_FIRST(&ic->ic_vaps); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* We always set the chandef to no-HT for scanning. */ + cfg80211_chandef_create(&lhw->scan_chandef, chan, + NL80211_CHAN_NO_HT); +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(ic, "%s:%d: initialized lhw->scan_chandef\n", + __func__, __LINE__); #endif - NL80211_CHAN_NO_HT); - error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL); - if (error != 0 && error != EOPNOTSUPP) { - ic_printf(ic, "ERROR: %s: config %#0x returned %d\n", - __func__, IEEE80211_CONF_CHANGE_CHANNEL, error); - /* XXX should we unroll to the previous chandef? */ - IMPROVE(); - } else { - /* Update radiotap channels as well. */ - lhw->rtap_tx.wt_chan_freq = htole16(c->ic_freq); - lhw->rtap_tx.wt_chan_flags = htole16(c->ic_flags); - lhw->rtap_rx.wr_chan_freq = htole16(c->ic_freq); - lhw->rtap_rx.wr_chan_flags = htole16(c->ic_flags); + /* + * This works for as long as we do not do BGSCANs; otherwise + * it'll have to be offchan work. + */ + chanctx_conf = lkpi_get_chanctx_conf(hw, vif); + changed = lkpi_init_chanctx_conf(hw, &lhw->scan_chandef, chanctx_conf); + error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true); + + TRACE_SCAN(ic, "scan_flags %b chan %d ???, error %d", + lhw->scan_flags, LKPI_LHW_SCAN_BITS, + c->ic_ieee, error); + + IMPROVE("max power for scanning; TODO in lkpi_80211_update_chandef"); + + } else if (lhw->emulate_chanctx) { + /* + * We do not set the channel here for normal chanctx operation. + * That's just a setup to fail. scan_to_auth will setup all the + * other neccessary options for this to work. + */ + struct lkpi_ic_set_channel_iter_arg chanctx_iter_arg = { + .chan = chan, + .chanctx_conf = NULL, + }; + struct cfg80211_chan_def chandef; + + lkpi_init_chandef(ic, &chandef, chan, c, false); + + ieee80211_iter_chan_contexts_mtx(hw, + lkpi_ic_set_channel_chanctx_iterf, &chanctx_iter_arg); + + if (chanctx_iter_arg.chanctx_conf == NULL) { + /* No chanctx found for this channel. */ + struct ieee80211vap *vap; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + + /* + * For now just pick the first VIF. + * net80211 will need to grow DBDC/link_id support + * for us to find the vif/chanctx otherwise. + */ + vap = TAILQ_FIRST(&ic->ic_vaps); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(ic, "%s:%d: using on stack chandef\n", + __func__, __LINE__); +#endif + chanctx_conf = lkpi_get_chanctx_conf(hw, vif); + changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf); + IMPROVE("update HT, VHT, bw, ..."); + error = lkpi_set_chanctx_conf(hw, vif, chanctx_conf, changed, true); + + } else { + /* + * We know we are on the same channel. + * Do we really have to reset everything? + */ + IMPROVE("update HT, VHT, bw, ..."); + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(ic, "%s:%d: using on stack chandef\n", + __func__, __LINE__); +#endif + + chanctx_conf = chanctx_iter_arg.chanctx_conf; + changed = lkpi_init_chanctx_conf(hw, &chandef, chanctx_conf); + lkpi_80211_mo_change_chanctx(hw, chanctx_conf, changed); + } } /* Currently PS is hard coded off! Not sure it belongs here. */ - IMPROVE(); + IMPROVE("PS"); if (ieee80211_hw_check(hw, SUPPORTS_PS) && (hw->conf.flags & IEEE80211_CONF_PS) != 0) { hw->conf.flags &= ~IEEE80211_CONF_PS; @@ -5068,6 +5330,8 @@ lkpi_ic_set_channel(struct ieee80211com *ic) "%d\n", __func__, IEEE80211_CONF_CHANGE_PS, error); } + + wiphy_unlock(hw->wiphy); } static struct ieee80211_node * @@ -6380,6 +6644,53 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) struct lkpi_hw *lhw; struct wiphy *wiphy; int ac; + bool emuchanctx; + + /* + * Do certain checks before starting to allocate resources. + * Store results in temporary variables. + */ + + /* ac1d519c01ca introduced emulating chanctx changes. */ + emuchanctx = false; + if (ops->add_chanctx == ieee80211_emulate_add_chanctx && + ops->change_chanctx == ieee80211_emulate_change_chanctx && + ops->remove_chanctx == ieee80211_emulate_remove_chanctx) { + /* + * If we emulate the chanctx ops, we must not have + * assign_vif_chanctx and unassign_vif_chanctx. + */ + if (ops->assign_vif_chanctx != NULL || + ops->unassign_vif_chanctx != NULL) { + /* Fail gracefully. */ + printf("%s: emulate_chanctx but " + "assign_vif_chanctx %p != NULL || " + "unassign_vif_chanctx %p != NULL\n", __func__, + ops->assign_vif_chanctx, ops->unassign_vif_chanctx); + return (NULL); + } + emuchanctx = true; + } + if (!emuchanctx && (ops->add_chanctx == ieee80211_emulate_add_chanctx || + ops->change_chanctx == ieee80211_emulate_change_chanctx || + ops->remove_chanctx == ieee80211_emulate_remove_chanctx)) { + printf("%s: not emulating chanctx changes but emulating " + "function set: %d/%d/%d\n", __func__, + ops->add_chanctx == ieee80211_emulate_add_chanctx, + ops->change_chanctx == ieee80211_emulate_change_chanctx, + ops->remove_chanctx == ieee80211_emulate_remove_chanctx); + return (NULL); + } + if (!emuchanctx && (ops->add_chanctx == NULL || ops->change_chanctx == NULL || + ops->remove_chanctx == NULL || ops->assign_vif_chanctx == NULL || + ops->unassign_vif_chanctx == NULL)) { + printf("%s: not all functions set for chanctx operations " + "(emulating chanctx %d): %p/%p/%p %p/%p\n", + __func__, emuchanctx, + ops->add_chanctx, ops->change_chanctx, ops->remove_chanctx, + ops->assign_vif_chanctx, ops->unassign_vif_chanctx); + return (NULL); + } /* Get us and the driver data also allocated. */ wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len); @@ -6404,6 +6715,8 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) /* Chanctx_conf */ INIT_LIST_HEAD(&lhw->lchanctx_list); + INIT_LIST_HEAD(&lhw->lchanctx_list_reserved); + lhw->emulate_chanctx = emuchanctx; /* Deferred RX path. */ LKPI_80211_LHW_RXQ_LOCK_INIT(lhw); @@ -6423,6 +6736,8 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) /* BSD Specific. */ lhw->ic = lkpi_ieee80211_ifalloc(); + if (lhw->emulate_chanctx) + ic_printf(lhw->ic, "Using chanctx emulation.\n"); IMPROVE(); return (hw); @@ -6478,6 +6793,7 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw) __func__, lhw, mbufq_len(&lhw->rxq))); LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw); + wiphy_lock(hw->wiphy); /* Chanctx_conf. */ if (!list_empty_careful(&lhw->lchanctx_list)) { struct lkpi_chanctx *lchanctx, *next; @@ -6490,9 +6806,21 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw) lkpi_80211_mo_remove_chanctx(hw, chanctx_conf); } list_del(&lchanctx->entry); + list_add_rcu(&lchanctx->entry, &lhw->lchanctx_list_reserved); + } + } + if (!list_empty_careful(&lhw->lchanctx_list_reserved)) { + struct lkpi_chanctx *lchanctx, *next; + + list_for_each_entry_safe(lchanctx, next, &lhw->lchanctx_list_reserved, entry) { + list_del(&lchanctx->entry); + if (lchanctx->added_to_drv) + panic("%s: lchanctx %p on reserved list still added_to_drv\n", + __func__, lchanctx); free(lchanctx, M_LKPI80211); } } + wiphy_unlock(hw->wiphy); LKPI_80211_LHW_MC_LOCK(lhw); lkpi_cleanup_mcast_list_locked(lhw); @@ -6793,6 +7121,13 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) (ic->ic_flags_ht & IEEE80211_FHT_HT) ? NL80211_CHAN_HT20 : #endif NL80211_CHAN_NO_HT); + lhw->dflt_chandef = hw->conf.chandef; +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(ic, "%s:%d: initialized " + "hw->conf.chandef and dflt_chandef to %p\n", + __func__, __LINE__, &lhw->dflt_chandef); +#endif break; } } @@ -7592,13 +7927,13 @@ no_trace_beacons: struct ieee80211_vif *vif; struct ieee80211_frame *wh; - wh = mtod(m, struct ieee80211_frame *); - if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) - goto skip_device_ts; - lvif = VAP_TO_LVIF(vap); vif = LVIF_TO_VIF(lvif); + wh = mtod(m, struct ieee80211_frame *); + if (!IEEE80211_ADDR_EQ(wh->i_addr2, vif->cfg.ap_addr)) + goto skip_device_ts; + IMPROVE("TIMING_BEACON_ONLY?"); /* mac80211 specific (not net80211) so keep it here. */ vif->bss_conf.sync_device_ts = rx_status->device_timestamp; @@ -8436,8 +8771,6 @@ struct sk_buff * linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int linkid, bool qos) { - struct lkpi_vif *lvif; - struct ieee80211vap *vap; struct sk_buff *skb; struct ieee80211_frame *nullf; @@ -8449,17 +8782,15 @@ linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw, skb_reserve(skb, hw->extra_tx_headroom); - lvif = VIF_TO_LVIF(vif); - vap = LVIF_TO_VAP(lvif); - nullf = skb_put_zero(skb, sizeof(*nullf)); nullf->i_fc[0] = IEEE80211_FC0_VERSION_0; nullf->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA; nullf->i_fc[1] = IEEE80211_FC1_DIR_TODS; - IEEE80211_ADDR_COPY(nullf->i_addr1, vap->iv_bss->ni_bssid); + /* XXX-BZ if link is given, this is different. */ + IEEE80211_ADDR_COPY(nullf->i_addr1, vif->cfg.ap_addr); IEEE80211_ADDR_COPY(nullf->i_addr2, vif->addr); - IEEE80211_ADDR_COPY(nullf->i_addr3, vap->iv_bss->ni_macaddr); + IEEE80211_ADDR_COPY(nullf->i_addr3, vif->cfg.ap_addr); return (skb); } @@ -8992,6 +9323,35 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy) /* -------------------------------------------------------------------------- */ +static bool +cfg80211_chan_def_are_same(struct cfg80211_chan_def *cd1, + struct cfg80211_chan_def *cd2) +{ + + if (cd1 == cd2) + return (true); + + if (cd1 == NULL || cd2 == NULL) + return (false); + + if (cd1->chan != cd2->chan) + return (false); + + if (cd1->width != cd2->width) + return (false); + + if (cd1->center_freq1 != cd2->center_freq1) + return (false); + + if (cd1->center_freq2 != cd2->center_freq2) + return (false); + + if (cd1->punctured != cd2->punctured) + return (false); + + return (true); +} + /* * hw->conf get initialized/set in various places for us: * - linuxkpi_ieee80211_alloc_hw(): flags @@ -9000,26 +9360,80 @@ linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy) * - lkpi_ic_set_channel(): chandef, flags */ -int lkpi_80211_update_chandef(struct ieee80211_hw *hw, +static int +lkpi_80211_update_chandef(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *new) { + struct lkpi_hw *lhw; struct cfg80211_chan_def *cd; uint32_t changed; int error; + bool same; - changed = 0; - if (new == NULL || new->def.chan == NULL) - cd = NULL; - else + lockdep_assert_wiphy(hw->wiphy); + + lhw = HW_TO_LHW(hw); + if (!lhw->emulate_chanctx) + return (0); + + if (new == NULL || new->def.chan == NULL) { + /* + * In case of remove "new" is NULL, we need to get us to some + * basic channel width but we'd also need to set the channel + * accordingly somewhere. + * The same is true if we are scanning in which case the + * scan_chandef should have a channel set. + */ + if (lhw->scan_chandef.chan != NULL) { +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(lhw->ic, "%s:%d: using scan_chandef %p\n", + __func__, __LINE__, &lhw->scan_chandef); +#endif + cd = &lhw->scan_chandef; + } else { +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(lhw->ic, "%s:%d: using dflt_chandef %p\n", + __func__, __LINE__, &lhw->dflt_chandef); +#endif + cd = &lhw->dflt_chandef; + } + } else { +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(lhw->ic, "%s:%d: using chanctx %p chandef %p\n", + __func__, __LINE__, new, &new->def); +#endif cd = &new->def; + } - if (cd && cd->chan != hw->conf.chandef.chan) { + changed = 0; + same = cfg80211_chan_def_are_same(cd, &hw->conf.chandef); + if (!same) { /* Copy; the chan pointer is fine and will stay valid. */ hw->conf.chandef = *cd; changed |= IEEE80211_CONF_CHANGE_CHANNEL; } IMPROVE("IEEE80211_CONF_CHANGE_PS, IEEE80211_CONF_CHANGE_POWER"); +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_CHANDEF) != 0) + ic_printf(lhw->ic, "%s:%d: chanctx %p { %u } cd %p { %u } " + "hw->conf.chandef %p { %u %d %u %u %u }, " + "changed %#04x same %d\n", + __func__, __LINE__, + new, (new != NULL && new->def.chan != NULL) ? + new->def.chan->center_freq : 0, + cd, cd->chan->center_freq, + &hw->conf.chandef, hw->conf.chandef.chan->center_freq, + hw->conf.chandef.width, + hw->conf.chandef.center_freq1, + hw->conf.chandef.center_freq2, + hw->conf.chandef.punctured, + changed, same); +#endif + if (changed == 0) return (0); @@ -9027,6 +9441,97 @@ int lkpi_80211_update_chandef(struct ieee80211_hw *hw, return (error); } +int +ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + int error; + + lockdep_assert_wiphy(hw->wiphy); + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) { + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n", + __func__, __LINE__, chanctx_conf); + } +#endif + + hw->conf.radar_enabled = chanctx_conf->radar_enabled; + error = lkpi_80211_update_chandef(hw, chanctx_conf); + return (error); +} + +void +ieee80211_emulate_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf __unused) +{ + + lockdep_assert_wiphy(hw->wiphy); + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) { + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n", + __func__, __LINE__, chanctx_conf); + } +#endif + + hw->conf.radar_enabled = false; + lkpi_80211_update_chandef(hw, NULL); +} + +void +ieee80211_emulate_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed __unused) +{ + + lockdep_assert_wiphy(hw->wiphy); + +#ifdef LINUXKPI_DEBUG_80211 + if ((linuxkpi_debug_80211 & D80211_TRACE) != 0) { + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + ic_printf(lhw->ic, "%s:%d: chanctx_conf %p\n", + __func__, __LINE__, chanctx_conf); + } +#endif + + hw->conf.radar_enabled = chanctx_conf->radar_enabled; + lkpi_80211_update_chandef(hw, chanctx_conf); +} + +int +ieee80211_emulate_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, int n_vifs, + enum ieee80211_chanctx_switch_mode mode __unused) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + int error; + + lockdep_assert_wiphy(hw->wiphy); + + /* Sanity check. */ + if (n_vifs <= 0) + return (-EINVAL); + if (vifs == NULL || vifs[0].new_ctx == NULL) + return (-EINVAL); + + /* + * What to do if n_vifs > 1? + * Does that make sense for drivers not supporting chanctx? + */ + hw->conf.radar_enabled = vifs[0].new_ctx->radar_enabled; + chanctx_conf = vifs[0].new_ctx; + error = lkpi_80211_update_chandef(hw, chanctx_conf); + return (error); +} + /* -------------------------------------------------------------------------- */ MODULE_VERSION(linuxkpi_wlan, 1); diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h index 8ae5c3d13d2d..569c4f12f6d6 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.h +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -57,6 +57,7 @@ #define D80211_IMPROVE 0x00000002 #endif #define D80211_IMPROVE_TXQ 0x00000004 +#define D80211_CHANDEF 0x00000008 #define D80211_TRACE 0x00000010 #define D80211_TRACEOK 0x00000020 #define D80211_SCAN 0x00000040 @@ -250,10 +251,14 @@ struct lkpi_hw { /* name it mac80211_sc? */ struct sx lvif_sx; struct list_head lchanctx_list; + struct list_head lchanctx_list_reserved; struct netdev_hw_addr_list mc_list; unsigned int mc_flags; struct sx mc_sx; + struct cfg80211_chan_def dflt_chandef; + struct cfg80211_chan_def scan_chandef; + struct mtx txq_mtx; uint32_t txq_generation[IEEE80211_NUM_ACS]; spinlock_t txq_scheduled_lock[IEEE80211_NUM_ACS]; @@ -314,6 +319,7 @@ struct lkpi_hw { /* name it mac80211_sc? */ bool mc_all_multi; bool update_wme; bool rxq_stopped; + bool emulate_chanctx; /* Must be last! */ struct ieee80211_hw hw __aligned(CACHE_LINE_SIZE); @@ -328,6 +334,7 @@ struct lkpi_chanctx { struct list_head entry; bool added_to_drv; /* Managed by MO */ + struct lkpi_vif *lvif; /* Backpointer. */ struct ieee80211_chanctx_conf chanctx_conf __aligned(CACHE_LINE_SIZE); }; diff --git a/sys/compat/linuxkpi/dummy/include/linux/mfd/core.h b/sys/compat/linuxkpi/dummy/include/linux/mfd/core.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/sys/compat/linuxkpi/dummy/include/linux/mfd/core.h +++ /dev/null |
