Loading drivers/net/wireless/ath/wil6210/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,17 @@ config WIL6210_DEBUGFS If unsure, say Y to make it easier to debug problems. config WIL6210_WRITE_IOCTL bool "wil6210 write ioctl to the device" depends on WIL6210 default y help Say Y here to allow write-access from user-space to the device memory through ioctl. This is useful for debugging purposes only. If unsure, say N. config WIL6210_IPA bool "wil6210 IPA offload support" depends on WIL6210 Loading drivers/net/wireless/ath/wil6210/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ wil6210-y += txrx.o wil6210-y += txrx_edma.o wil6210-y += debug.o wil6210-y += rx_reorder.o wil6210-y += ioctl.o wil6210-y += fw.o wil6210-y += pm.o wil6210-y += pmc.o Loading drivers/net/wireless/ath/wil6210/ioctl.c 0 → 100644 +245 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: ISC /* Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/uaccess.h> #include "wil6210.h" #include <uapi/linux/wil6210_uapi.h> #define wil_hex_dump_ioctl(prefix_str, buf, len) \ print_hex_dump_debug("DBG[IOC ]" prefix_str, \ DUMP_PREFIX_OFFSET, 16, 1, buf, len, true) #define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg) #define WIL_PRIV_DATA_MAX_LEN 8192 #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" struct wil_android_priv_data { char *buf; int used_len; int total_len; }; static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, u32 addr, u32 size, u32 op) { void __iomem *a; u32 off; switch (op & WIL_MMIO_ADDR_MASK) { case WIL_MMIO_ADDR_LINKER: a = wmi_buffer(wil, cpu_to_le32(addr)); break; case WIL_MMIO_ADDR_AHB: a = wmi_addr(wil, addr); break; case WIL_MMIO_ADDR_BAR: a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF); break; default: wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op); return NULL; } off = a - wil->csr; if (size > wil->bar_size - off) { wil_err(wil, "Invalid requested block: off(0x%08x) size(0x%08x)\n", off, size); return NULL; } return a; } static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data) { struct wil_memio io; void __iomem *a; bool need_copy = false; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n", io.addr, io.val, io.op); a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } /* operation */ switch (io.op & WIL_MMIO_OP_MASK) { case WIL_MMIO_READ: io.val = readl_relaxed(a); need_copy = true; break; #if defined(CONFIG_WIL6210_WRITE_IOCTL) case WIL_MMIO_WRITE: writel_relaxed(io.val, a); wmb(); /* make sure write propagated to HW */ break; #endif default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); return -EINVAL; } if (need_copy) { wil_dbg_ioctl(wil, "IO done: addr(0x%08x) val(0x%08x) op(0x%08x)\n", io.addr, io.val, io.op); if (copy_to_user(data, &io, sizeof(io))) return -EFAULT; } return 0; } static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data) { struct wil_memio_block io; void *block; void __iomem *a; int rc = 0; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n", io.addr, io.size, io.op); /* size */ if (io.size > WIL6210_MAX_MEM_SIZE) { wil_err(wil, "size is too large: 0x%08x\n", io.size); return -EINVAL; } if (io.size % 4) { wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size); return -EINVAL; } a = wil_ioc_addr(wil, io.addr, io.size, io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } block = kmalloc(io.size, GFP_USER); if (!block) return -ENOMEM; /* operation */ switch (io.op & WIL_MMIO_OP_MASK) { case WIL_MMIO_READ: wil_memcpy_fromio_32(block, a, io.size); wil_hex_dump_ioctl("Read ", block, io.size); if (copy_to_user((void __user *)(uintptr_t)io.block, block, io.size)) { rc = -EFAULT; goto out_free; } break; #if defined(CONFIG_WIL6210_WRITE_IOCTL) case WIL_MMIO_WRITE: if (copy_from_user(block, (void __user *)(uintptr_t)io.block, io.size)) { rc = -EFAULT; goto out_free; } wil_memcpy_toio_32(a, block, io.size); wmb(); /* make sure write propagated to HW */ wil_hex_dump_ioctl("Write ", block, io.size); break; #endif default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); rc = -EINVAL; break; } out_free: kfree(block); return rc; } static int wil_ioc_android(struct wil6210_priv *wil, void __user *data) { int rc = 0; char *command; struct wil_android_priv_data priv_data; wil_dbg_ioctl(wil, "ioc_android\n"); if (copy_from_user(&priv_data, data, sizeof(priv_data))) return -EFAULT; if (priv_data.total_len <= 0 || priv_data.total_len >= WIL_PRIV_DATA_MAX_LEN) { wil_err(wil, "invalid data len %d\n", priv_data.total_len); return -EINVAL; } command = kmalloc(priv_data.total_len + 1, GFP_KERNEL); if (!command) return -ENOMEM; if (copy_from_user(command, priv_data.buf, priv_data.total_len)) { rc = -EFAULT; goto out_free; } /* Make sure the command is NUL-terminated */ command[priv_data.total_len] = '\0'; wil_dbg_ioctl(wil, "ioc_android: command = %s\n", command); /* P2P not supported, but WPS is (in AP mode). * Ignore those in order not to block WPS functionality * in non-P2P mode. */ if (strncasecmp(command, CMD_SET_AP_WPS_P2P_IE, strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) rc = 0; else rc = -ENOIOCTLCMD; out_free: kfree(command); return rc; } int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd) { int ret; ret = wil_pm_runtime_get(wil); if (ret < 0) return ret; switch (cmd) { case WIL_IOCTL_MEMIO: ret = wil_ioc_memio_dword(wil, data); break; case WIL_IOCTL_MEMIO_BLOCK: ret = wil_ioc_memio_block(wil, data); break; case (SIOCDEVPRIVATE + 1): ret = wil_ioc_android(wil, data); break; default: wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd); wil_pm_runtime_put(wil); return -ENOIOCTLCMD; } wil_pm_runtime_put(wil); wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret); return ret; } drivers/net/wireless/ath/wil6210/netdev.c +9 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/moduleparam.h> Loading Loading @@ -94,6 +94,13 @@ static int wil_stop(struct net_device *ndev) return rc; } static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) { struct wil6210_priv *wil = ndev_to_wil(ndev); return wil_ioctl(wil, ifr->ifr_data, cmd); } /** * AC to queue mapping * Loading Loading @@ -134,6 +141,7 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_select_queue = wil_select_queue, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = wil_do_ioctl, }; static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) Loading drivers/net/wireless/ath/wil6210/wil6210.h +1 −0 Original line number Diff line number Diff line Loading @@ -1476,6 +1476,7 @@ void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, int wil_iftype_nl2wmi(enum nl80211_iftype type); int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd); int wil_request_firmware(struct wil6210_priv *wil, const char *name, bool load); int wil_request_board(struct wil6210_priv *wil, const char *name); Loading Loading
drivers/net/wireless/ath/wil6210/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,17 @@ config WIL6210_DEBUGFS If unsure, say Y to make it easier to debug problems. config WIL6210_WRITE_IOCTL bool "wil6210 write ioctl to the device" depends on WIL6210 default y help Say Y here to allow write-access from user-space to the device memory through ioctl. This is useful for debugging purposes only. If unsure, say N. config WIL6210_IPA bool "wil6210 IPA offload support" depends on WIL6210 Loading
drivers/net/wireless/ath/wil6210/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ wil6210-y += txrx.o wil6210-y += txrx_edma.o wil6210-y += debug.o wil6210-y += rx_reorder.o wil6210-y += ioctl.o wil6210-y += fw.o wil6210-y += pm.o wil6210-y += pmc.o Loading
drivers/net/wireless/ath/wil6210/ioctl.c 0 → 100644 +245 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: ISC /* Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/uaccess.h> #include "wil6210.h" #include <uapi/linux/wil6210_uapi.h> #define wil_hex_dump_ioctl(prefix_str, buf, len) \ print_hex_dump_debug("DBG[IOC ]" prefix_str, \ DUMP_PREFIX_OFFSET, 16, 1, buf, len, true) #define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg) #define WIL_PRIV_DATA_MAX_LEN 8192 #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" struct wil_android_priv_data { char *buf; int used_len; int total_len; }; static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, u32 addr, u32 size, u32 op) { void __iomem *a; u32 off; switch (op & WIL_MMIO_ADDR_MASK) { case WIL_MMIO_ADDR_LINKER: a = wmi_buffer(wil, cpu_to_le32(addr)); break; case WIL_MMIO_ADDR_AHB: a = wmi_addr(wil, addr); break; case WIL_MMIO_ADDR_BAR: a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF); break; default: wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op); return NULL; } off = a - wil->csr; if (size > wil->bar_size - off) { wil_err(wil, "Invalid requested block: off(0x%08x) size(0x%08x)\n", off, size); return NULL; } return a; } static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data) { struct wil_memio io; void __iomem *a; bool need_copy = false; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n", io.addr, io.val, io.op); a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } /* operation */ switch (io.op & WIL_MMIO_OP_MASK) { case WIL_MMIO_READ: io.val = readl_relaxed(a); need_copy = true; break; #if defined(CONFIG_WIL6210_WRITE_IOCTL) case WIL_MMIO_WRITE: writel_relaxed(io.val, a); wmb(); /* make sure write propagated to HW */ break; #endif default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); return -EINVAL; } if (need_copy) { wil_dbg_ioctl(wil, "IO done: addr(0x%08x) val(0x%08x) op(0x%08x)\n", io.addr, io.val, io.op); if (copy_to_user(data, &io, sizeof(io))) return -EFAULT; } return 0; } static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data) { struct wil_memio_block io; void *block; void __iomem *a; int rc = 0; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n", io.addr, io.size, io.op); /* size */ if (io.size > WIL6210_MAX_MEM_SIZE) { wil_err(wil, "size is too large: 0x%08x\n", io.size); return -EINVAL; } if (io.size % 4) { wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size); return -EINVAL; } a = wil_ioc_addr(wil, io.addr, io.size, io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } block = kmalloc(io.size, GFP_USER); if (!block) return -ENOMEM; /* operation */ switch (io.op & WIL_MMIO_OP_MASK) { case WIL_MMIO_READ: wil_memcpy_fromio_32(block, a, io.size); wil_hex_dump_ioctl("Read ", block, io.size); if (copy_to_user((void __user *)(uintptr_t)io.block, block, io.size)) { rc = -EFAULT; goto out_free; } break; #if defined(CONFIG_WIL6210_WRITE_IOCTL) case WIL_MMIO_WRITE: if (copy_from_user(block, (void __user *)(uintptr_t)io.block, io.size)) { rc = -EFAULT; goto out_free; } wil_memcpy_toio_32(a, block, io.size); wmb(); /* make sure write propagated to HW */ wil_hex_dump_ioctl("Write ", block, io.size); break; #endif default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); rc = -EINVAL; break; } out_free: kfree(block); return rc; } static int wil_ioc_android(struct wil6210_priv *wil, void __user *data) { int rc = 0; char *command; struct wil_android_priv_data priv_data; wil_dbg_ioctl(wil, "ioc_android\n"); if (copy_from_user(&priv_data, data, sizeof(priv_data))) return -EFAULT; if (priv_data.total_len <= 0 || priv_data.total_len >= WIL_PRIV_DATA_MAX_LEN) { wil_err(wil, "invalid data len %d\n", priv_data.total_len); return -EINVAL; } command = kmalloc(priv_data.total_len + 1, GFP_KERNEL); if (!command) return -ENOMEM; if (copy_from_user(command, priv_data.buf, priv_data.total_len)) { rc = -EFAULT; goto out_free; } /* Make sure the command is NUL-terminated */ command[priv_data.total_len] = '\0'; wil_dbg_ioctl(wil, "ioc_android: command = %s\n", command); /* P2P not supported, but WPS is (in AP mode). * Ignore those in order not to block WPS functionality * in non-P2P mode. */ if (strncasecmp(command, CMD_SET_AP_WPS_P2P_IE, strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) rc = 0; else rc = -ENOIOCTLCMD; out_free: kfree(command); return rc; } int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd) { int ret; ret = wil_pm_runtime_get(wil); if (ret < 0) return ret; switch (cmd) { case WIL_IOCTL_MEMIO: ret = wil_ioc_memio_dword(wil, data); break; case WIL_IOCTL_MEMIO_BLOCK: ret = wil_ioc_memio_block(wil, data); break; case (SIOCDEVPRIVATE + 1): ret = wil_ioc_android(wil, data); break; default: wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd); wil_pm_runtime_put(wil); return -ENOIOCTLCMD; } wil_pm_runtime_put(wil); wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret); return ret; }
drivers/net/wireless/ath/wil6210/netdev.c +9 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include <linux/moduleparam.h> Loading Loading @@ -94,6 +94,13 @@ static int wil_stop(struct net_device *ndev) return rc; } static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) { struct wil6210_priv *wil = ndev_to_wil(ndev); return wil_ioctl(wil, ifr->ifr_data, cmd); } /** * AC to queue mapping * Loading Loading @@ -134,6 +141,7 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_select_queue = wil_select_queue, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = wil_do_ioctl, }; static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) Loading
drivers/net/wireless/ath/wil6210/wil6210.h +1 −0 Original line number Diff line number Diff line Loading @@ -1476,6 +1476,7 @@ void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, int wil_iftype_nl2wmi(enum nl80211_iftype type); int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd); int wil_request_firmware(struct wil6210_priv *wil, const char *name, bool load); int wil_request_board(struct wil6210_priv *wil, const char *name); Loading