Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 = readl(a);
 80		need_copy = true;
 81		break;
 82	case wil_mmio_write:
 83		writel(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	int ret;
165
166	switch (cmd) {
167	case WIL_IOCTL_MEMIO:
168		ret = wil_ioc_memio_dword(wil, data);
169		break;
170	case WIL_IOCTL_MEMIO_BLOCK:
171		ret = wil_ioc_memio_block(wil, data);
172		break;
173	default:
174		wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
175		return -ENOIOCTLCMD;
176	}
177
178	wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
179	return ret;
180}