wil6210: atomic I/O for the card memory
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Wed, 1 Oct 2014 12:05:25 +0000 (15:05 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 2 Oct 2014 18:23:14 +0000 (14:23 -0400)
Introduce netdev IOCTLs, to be used by the debug tools.

Allows to read/write single dword value or
memory block, aligned to dword
Different address modes supported:
- BAR offset
- Firmware "linker" address
- target's AHB bus

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
MAINTAINERS
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/ioctl.c [new file with mode: 0644]
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/wil6210.h
include/uapi/linux/wil6210_uapi.h [new file with mode: 0644]

index 482888d804313b68a19da850b12434cd56e707f1..66de6b2923d49f133dd4b12d9ba04e4d0cc7bcfe 100644 (file)
@@ -1611,6 +1611,7 @@ L:        wil6210@qca.qualcomm.com
 S:     Supported
 W:     http://wireless.kernel.org/en/users/Drivers/wil6210
 F:     drivers/net/wireless/ath/wil6210/
+F:     include/uapi/linux/wil6210_uapi.h
 
 CARL9170 LINUX COMMUNITY WIRELESS DRIVER
 M:     Christian Lamparter <chunkeey@googlemail.com>
index 05f29a6b1ba84bce43ecfa3282eeb672662d988e..8ad4b5f97e043528d3661b7fc23010eb9b9ef17e 100644 (file)
@@ -10,6 +10,7 @@ wil6210-y += interrupt.o
 wil6210-y += txrx.o
 wil6210-y += debug.o
 wil6210-y += rx_reorder.o
+wil6210-y += ioctl.o
 wil6210-y += fw.o
 wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
 wil6210-y += wil_platform.o
diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c
new file mode 100644 (file)
index 0000000..e9c0673
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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)
+
+static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr,
+                                 uint32_t size, enum wil_memio_op 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 >= WIL6210_MEM_SIZE - off) {
+               wil_err(wil, "Requested block does not fit into memory: "
+                       "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 = ioread32(a);
+               need_copy = true;
+               break;
+       case wil_mmio_write:
+               iowrite32(io.val, a);
+               wmb(); /* make sure write propagated to HW */
+               break;
+       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 % 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(io.block, block, io.size)) {
+                       rc = -EFAULT;
+                       goto out_free;
+               }
+               break;
+       case wil_mmio_write:
+               if (copy_from_user(block, 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;
+       default:
+               wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
+               rc = -EINVAL;
+               break;
+       }
+
+out_free:
+       kfree(block);
+       return rc;
+}
+
+int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd)
+{
+       switch (cmd) {
+       case WIL_IOCTL_MEMIO:
+               return wil_ioc_memio_dword(wil, data);
+       case WIL_IOCTL_MEMIO_BLOCK:
+               return wil_ioc_memio_block(wil, data);
+       default:
+               wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
+               return -ENOIOCTLCMD;
+       }
+}
index c3f0ddfaa5da507461975d5708f832136f5b29b7..239965106c05d1f878f52cd8b552047d2743d998 100644 (file)
@@ -52,6 +52,17 @@ static int wil_change_mtu(struct net_device *ndev, int new_mtu)
        return 0;
 }
 
+static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+       int ret = wil_ioctl(wil, ifr->ifr_data, cmd);
+
+       wil_dbg_misc(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
+
+       return ret;
+}
+
 static const struct net_device_ops wil_netdev_ops = {
        .ndo_open               = wil_open,
        .ndo_stop               = wil_stop,
@@ -59,6 +70,7 @@ static const struct net_device_ops wil_netdev_ops = {
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_change_mtu         = wil_change_mtu,
+       .ndo_do_ioctl           = wil_do_ioctl,
 };
 
 static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
index 2991609885f7971b4f206915f27d2b70e5bb3635..61cceca155a2f4943760dd4e079e0a7341abef0e 100644 (file)
@@ -595,5 +595,7 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
 
 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);
+
 #endif /* __WIL6210_H__ */
diff --git a/include/uapi/linux/wil6210_uapi.h b/include/uapi/linux/wil6210_uapi.h
new file mode 100644 (file)
index 0000000..6a3cddd
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __WIL6210_UAPI_H__
+#define __WIL6210_UAPI_H__
+
+#if !defined(__KERNEL__)
+#define __user
+#endif
+
+#include <linux/sockios.h>
+
+/* Numbers SIOCDEVPRIVATE and SIOCDEVPRIVATE + 1
+ * are used by Android devices to implement PNO (preferred network offload).
+ * Albeit it is temporary solution, use different numbers to avoid conflicts
+ */
+
+/**
+ * Perform 32-bit I/O operation to the card memory
+ *
+ * User code should arrange data in memory like this:
+ *
+ *     struct wil_memio io;
+ *     struct ifreq ifr = {
+ *             .ifr_data = &io,
+ *     };
+ */
+#define WIL_IOCTL_MEMIO (SIOCDEVPRIVATE + 2)
+
+/**
+ * Perform block I/O operation to the card memory
+ *
+ * User code should arrange data in memory like this:
+ *
+ *     void *buf;
+ *     struct wil_memio_block io = {
+ *             .block = buf,
+ *     };
+ *     struct ifreq ifr = {
+ *             .ifr_data = &io,
+ *     };
+ */
+#define WIL_IOCTL_MEMIO_BLOCK (SIOCDEVPRIVATE + 3)
+
+/**
+ * operation to perform
+ *
+ * @wil_mmio_op_mask - bits defining operation,
+ * @wil_mmio_addr_mask - bits defining addressing mode
+ */
+enum wil_memio_op {
+       wil_mmio_read = 0,
+       wil_mmio_write = 1,
+       wil_mmio_op_mask = 0xff,
+       wil_mmio_addr_linker = 0 << 8,
+       wil_mmio_addr_ahb = 1 << 8,
+       wil_mmio_addr_bar = 2 << 8,
+       wil_mmio_addr_mask = 0xff00,
+};
+
+struct wil_memio {
+       uint32_t op; /* enum wil_memio_op */
+       uint32_t addr; /* should be 32-bit aligned */
+       uint32_t val;
+};
+
+struct wil_memio_block {
+       uint32_t op; /* enum wil_memio_op */
+       uint32_t addr; /* should be 32-bit aligned */
+       uint32_t size; /* should be multiple of 4 */
+       void __user *block; /* block address */
+};
+
+#endif /* __WIL6210_UAPI_H__ */