aboutsummaryrefslogtreecommitdiff
path: root/sys/compat
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c2
-rw-r--r--sys/compat/linprocfs/linprocfs.c33
-rw-r--r--sys/compat/linux/linux_ioctl.c115
-rw-r--r--sys/compat/linux/linux_ioctl.h24
-rw-r--r--sys/compat/linux/linux_socket.c187
-rw-r--r--sys/compat/linux/linux_socket.h3
-rw-r--r--sys/compat/linux/linux_stats.c30
-rw-r--r--sys/compat/linuxkpi/common/include/asm/pgtable.h34
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ascii85.h46
-rw-r--r--sys/compat/linuxkpi/common/include/linux/compiler_types.h13
-rw-r--r--sys/compat/linuxkpi/common/include/linux/device.h7
-rw-r--r--sys/compat/linuxkpi/common/include/linux/gfp.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/hex.h44
-rw-r--r--sys/compat/linuxkpi/common/include/linux/instruction_pointer.h15
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ioport.h1
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kconfig.h35
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kernel.h35
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kfifo.h4
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kmsg_dump.h42
-rw-r--r--sys/compat/linuxkpi/common/include/linux/linux_logo.h19
-rw-r--r--sys/compat/linuxkpi/common/include/linux/mfd/core.h49
-rw-r--r--sys/compat/linuxkpi/common/include/linux/minmax.h3
-rw-r--r--sys/compat/linuxkpi/common/include/linux/overflow.h144
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sizes.h11
-rw-r--r--sys/compat/linuxkpi/common/include/linux/slab.h24
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string.h16
-rw-r--r--sys/compat/linuxkpi/common/include/linux/sysfs.h34
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h72
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c817
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h7
-rw-r--r--sys/compat/linuxkpi/dummy/include/linux/mfd/core.h0
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