1/* 2 * Copyright (c) 2014 Qualcomm Atheros, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <linux/uaccess.h> 18 19#include "wil6210.h" 20#include <uapi/linux/wil6210_uapi.h> 21 22#define wil_hex_dump_ioctl(prefix_str, buf, len) \ 23 print_hex_dump_debug("DBG[IOC ]" prefix_str, \ 24 DUMP_PREFIX_OFFSET, 16, 1, buf, len, true) 25#define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg) 26 27static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr, 28 uint32_t size, enum wil_memio_op op) 29{ 30 void __iomem *a; 31 u32 off; 32 33 switch (op & wil_mmio_addr_mask) { 34 case wil_mmio_addr_linker: 35 a = wmi_buffer(wil, cpu_to_le32(addr)); 36 break; 37 case wil_mmio_addr_ahb: 38 a = wmi_addr(wil, addr); 39 break; 40 case wil_mmio_addr_bar: 41 a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF); 42 break; 43 default: 44 wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op); 45 return NULL; 46 } 47 48 off = a - wil->csr; 49 if (size >= WIL6210_MEM_SIZE - off) { 50 wil_err(wil, "Requested block does not fit into memory: " 51 "off = 0x%08x size = 0x%08x\n", off, size); 52 return NULL; 53 } 54 55 return a; 56} 57 58static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data) 59{ 60 struct wil_memio io; 61 void __iomem *a; 62 bool need_copy = false; 63 64 if (copy_from_user(&io, data, sizeof(io))) 65 return -EFAULT; 66 67 wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n", 68 io.addr, io.val, io.op); 69 70 a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op); 71 if (!a) { 72 wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, 73 io.op); 74 return -EINVAL; 75 } 76 /* operation */ 77 switch (io.op & wil_mmio_op_mask) { 78 case wil_mmio_read: 79 io.val = ioread32(a); 80 need_copy = true; 81 break; 82 case wil_mmio_write: 83 iowrite32(io.val, a); 84 wmb(); /* make sure write propagated to HW */ 85 break; 86 default: 87 wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); 88 return -EINVAL; 89 } 90 91 if (need_copy) { 92 wil_dbg_ioctl(wil, "IO done: addr = 0x%08x" 93 " val = 0x%08x op = 0x%08x\n", 94 io.addr, io.val, io.op); 95 if (copy_to_user(data, &io, sizeof(io))) 96 return -EFAULT; 97 } 98 99 return 0; 100} 101 102static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data) 103{ 104 struct wil_memio_block io; 105 void *block; 106 void __iomem *a; 107 int rc = 0; 108 109 if (copy_from_user(&io, data, sizeof(io))) 110 return -EFAULT; 111 112 wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n", 113 io.addr, io.size, io.op); 114 115 /* size */ 116 if (io.size % 4) { 117 wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size); 118 return -EINVAL; 119 } 120 121 a = wil_ioc_addr(wil, io.addr, io.size, io.op); 122 if (!a) { 123 wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, 124 io.op); 125 return -EINVAL; 126 } 127 128 block = kmalloc(io.size, GFP_USER); 129 if (!block) 130 return -ENOMEM; 131 132 /* operation */ 133 switch (io.op & wil_mmio_op_mask) { 134 case wil_mmio_read: 135 wil_memcpy_fromio_32(block, a, io.size); 136 wil_hex_dump_ioctl("Read ", block, io.size); 137 if (copy_to_user(io.block, block, io.size)) { 138 rc = -EFAULT; 139 goto out_free; 140 } 141 break; 142 case wil_mmio_write: 143 if (copy_from_user(block, io.block, io.size)) { 144 rc = -EFAULT; 145 goto out_free; 146 } 147 wil_memcpy_toio_32(a, block, io.size); 148 wmb(); /* make sure write propagated to HW */ 149 wil_hex_dump_ioctl("Write ", block, io.size); 150 break; 151 default: 152 wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); 153 rc = -EINVAL; 154 break; 155 } 156 157out_free: 158 kfree(block); 159 return rc; 160} 161 162int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd) 163{ 164 switch (cmd) { 165 case WIL_IOCTL_MEMIO: 166 return wil_ioc_memio_dword(wil, data); 167 case WIL_IOCTL_MEMIO_BLOCK: 168 return wil_ioc_memio_block(wil, data); 169 default: 170 wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd); 171 return -ENOIOCTLCMD; 172 } 173} 174