123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852 |
- /******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
- #include <linux/export.h>
- #include <net/netlink.h>
- #include "iwl-io.h"
- #include "iwl-fh.h"
- #include "iwl-prph.h"
- #include "iwl-trans.h"
- #include "iwl-test.h"
- #include "iwl-csr.h"
- #include "iwl-testmode.h"
- /*
- * Periphery registers absolute lower bound. This is used in order to
- * differentiate registery access through HBUS_TARG_PRPH_* and
- * HBUS_TARG_MEM_* accesses.
- */
- #define IWL_ABS_PRPH_START (0xA00000)
- /*
- * The TLVs used in the gnl message policy between the kernel module and
- * user space application. iwl_testmode_gnl_msg_policy is to be carried
- * through the NL80211_CMD_TESTMODE channel regulated by nl80211.
- * See iwl-testmode.h
- */
- static
- struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
- [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, },
- [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, },
- [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, },
- [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, },
- [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, },
- [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
- [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, },
- [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, },
- [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, },
- };
- static inline void iwl_test_trace_clear(struct iwl_test *tst)
- {
- memset(&tst->trace, 0, sizeof(struct iwl_test_trace));
- }
- static void iwl_test_trace_stop(struct iwl_test *tst)
- {
- if (!tst->trace.enabled)
- return;
- if (tst->trace.cpu_addr && tst->trace.dma_addr)
- dma_free_coherent(tst->trans->dev,
- tst->trace.tsize,
- tst->trace.cpu_addr,
- tst->trace.dma_addr);
- iwl_test_trace_clear(tst);
- }
- static inline void iwl_test_mem_clear(struct iwl_test *tst)
- {
- memset(&tst->mem, 0, sizeof(struct iwl_test_mem));
- }
- static inline void iwl_test_mem_stop(struct iwl_test *tst)
- {
- if (!tst->mem.in_read)
- return;
- iwl_test_mem_clear(tst);
- }
- /*
- * Initializes the test object
- * During the lifetime of the test object it is assumed that the transport is
- * started. The test object should be stopped before the transport is stopped.
- */
- void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans,
- struct iwl_test_ops *ops)
- {
- tst->trans = trans;
- tst->ops = ops;
- iwl_test_trace_clear(tst);
- iwl_test_mem_clear(tst);
- }
- EXPORT_SYMBOL_GPL(iwl_test_init);
- /*
- * Stop the test object
- */
- void iwl_test_free(struct iwl_test *tst)
- {
- iwl_test_mem_stop(tst);
- iwl_test_trace_stop(tst);
- }
- EXPORT_SYMBOL_GPL(iwl_test_free);
- static inline int iwl_test_send_cmd(struct iwl_test *tst,
- struct iwl_host_cmd *cmd)
- {
- return tst->ops->send_cmd(tst->trans->op_mode, cmd);
- }
- static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr)
- {
- return tst->ops->valid_hw_addr(addr);
- }
- static inline u32 iwl_test_fw_ver(struct iwl_test *tst)
- {
- return tst->ops->get_fw_ver(tst->trans->op_mode);
- }
- static inline struct sk_buff*
- iwl_test_alloc_reply(struct iwl_test *tst, int len)
- {
- return tst->ops->alloc_reply(tst->trans->op_mode, len);
- }
- static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb)
- {
- return tst->ops->reply(tst->trans->op_mode, skb);
- }
- static inline struct sk_buff*
- iwl_test_alloc_event(struct iwl_test *tst, int len)
- {
- return tst->ops->alloc_event(tst->trans->op_mode, len);
- }
- static inline void
- iwl_test_event(struct iwl_test *tst, struct sk_buff *skb)
- {
- return tst->ops->event(tst->trans->op_mode, skb);
- }
- /*
- * This function handles the user application commands to the fw. The fw
- * commands are sent in a synchronuous manner. In case that the user requested
- * to get commands response, it is send to the user.
- */
- static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb)
- {
- struct iwl_host_cmd cmd;
- struct iwl_rx_packet *pkt;
- struct sk_buff *skb;
- void *reply_buf;
- u32 reply_len;
- int ret;
- bool cmd_want_skb;
- memset(&cmd, 0, sizeof(struct iwl_host_cmd));
- if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] ||
- !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) {
- IWL_ERR(tst->trans, "Missing fw command mandatory fields\n");
- return -ENOMSG;
- }
- cmd.flags = CMD_ON_DEMAND | CMD_SYNC;
- cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]);
- if (cmd_want_skb)
- cmd.flags |= CMD_WANT_SKB;
- cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
- cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
- cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n",
- cmd.id, cmd.flags, cmd.len[0]);
- ret = iwl_test_send_cmd(tst, &cmd);
- if (ret) {
- IWL_ERR(tst->trans, "Failed to send hcmd\n");
- return ret;
- }
- if (!cmd_want_skb)
- return ret;
- /* Handling return of SKB to the user */
- pkt = cmd.resp_pkt;
- if (!pkt) {
- IWL_ERR(tst->trans, "HCMD received a null response packet\n");
- return ret;
- }
- reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- skb = iwl_test_alloc_reply(tst, reply_len + 20);
- reply_buf = kmalloc(reply_len, GFP_KERNEL);
- if (!skb || !reply_buf) {
- kfree_skb(skb);
- kfree(reply_buf);
- return -ENOMEM;
- }
- /* The reply is in a page, that we cannot send to user space. */
- memcpy(reply_buf, &(pkt->hdr), reply_len);
- iwl_free_resp(&cmd);
- if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
- IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
- nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf))
- goto nla_put_failure;
- return iwl_test_reply(tst, skb);
- nla_put_failure:
- IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n");
- kfree(reply_buf);
- kfree_skb(skb);
- return -ENOMSG;
- }
- /*
- * Handles the user application commands for register access.
- */
- static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb)
- {
- u32 ofs, val32, cmd;
- u8 val8;
- struct sk_buff *skb;
- int status = 0;
- struct iwl_trans *trans = tst->trans;
- if (!tb[IWL_TM_ATTR_REG_OFFSET]) {
- IWL_ERR(trans, "Missing reg offset\n");
- return -ENOMSG;
- }
- ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]);
- IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs);
- cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
- /*
- * Allow access only to FH/CSR/HBUS in direct mode.
- * Since we don't have the upper bounds for the CSR and HBUS segments,
- * we will use only the upper bound of FH for sanity check.
- */
- if (ofs >= FH_MEM_UPPER_BOUND) {
- IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n",
- FH_MEM_UPPER_BOUND);
- return -EINVAL;
- }
- switch (cmd) {
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
- val32 = iwl_read_direct32(tst->trans, ofs);
- IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32);
- skb = iwl_test_alloc_reply(tst, 20);
- if (!skb) {
- IWL_ERR(trans, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32))
- goto nla_put_failure;
- status = iwl_test_reply(tst, skb);
- if (status < 0)
- IWL_ERR(trans, "Error sending msg : %d\n", status);
- break;
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
- if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
- IWL_ERR(trans, "Missing value to write\n");
- return -ENOMSG;
- } else {
- val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
- IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32);
- iwl_write_direct32(tst->trans, ofs, val32);
- }
- break;
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
- if (!tb[IWL_TM_ATTR_REG_VALUE8]) {
- IWL_ERR(trans, "Missing value to write\n");
- return -ENOMSG;
- } else {
- val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
- IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8);
- iwl_write8(tst->trans, ofs, val8);
- }
- break;
- default:
- IWL_ERR(trans, "Unknown test register cmd ID\n");
- return -ENOMSG;
- }
- return status;
- nla_put_failure:
- kfree_skb(skb);
- return -EMSGSIZE;
- }
- /*
- * Handles the request to start FW tracing. Allocates of the trace buffer
- * and sends a reply to user space with the address of the allocated buffer.
- */
- static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb)
- {
- struct sk_buff *skb;
- int status = 0;
- if (tst->trace.enabled)
- return -EBUSY;
- if (!tb[IWL_TM_ATTR_TRACE_SIZE])
- tst->trace.size = TRACE_BUFF_SIZE_DEF;
- else
- tst->trace.size =
- nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
- if (!tst->trace.size)
- return -EINVAL;
- if (tst->trace.size < TRACE_BUFF_SIZE_MIN ||
- tst->trace.size > TRACE_BUFF_SIZE_MAX)
- return -EINVAL;
- tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD;
- tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev,
- tst->trace.tsize,
- &tst->trace.dma_addr,
- GFP_KERNEL);
- if (!tst->trace.cpu_addr)
- return -ENOMEM;
- tst->trace.enabled = true;
- tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100);
- memset(tst->trace.trace_addr, 0x03B, tst->trace.size);
- skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20);
- if (!skb) {
- IWL_ERR(tst->trans, "Memory allocation fail\n");
- iwl_test_trace_stop(tst);
- return -ENOMEM;
- }
- if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR,
- sizeof(tst->trace.dma_addr),
- (u64 *)&tst->trace.dma_addr))
- goto nla_put_failure;
- status = iwl_test_reply(tst, skb);
- if (status < 0)
- IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
- tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size,
- DUMP_CHUNK_SIZE);
- return status;
- nla_put_failure:
- kfree_skb(skb);
- if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) ==
- IWL_TM_CMD_APP2DEV_BEGIN_TRACE)
- iwl_test_trace_stop(tst);
- return -EMSGSIZE;
- }
- /*
- * Handles indirect read from the periphery or the SRAM. The read is performed
- * to a temporary buffer. The user space application should later issue a dump
- */
- static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
- {
- struct iwl_trans *trans = tst->trans;
- unsigned long flags;
- int i;
- if (size & 0x3)
- return -EINVAL;
- tst->mem.size = size;
- tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL);
- if (tst->mem.addr == NULL)
- return -ENOMEM;
- /* Hard-coded periphery absolute address */
- if (IWL_ABS_PRPH_START <= addr &&
- addr < IWL_ABS_PRPH_START + PRPH_END) {
- if (!iwl_trans_grab_nic_access(trans, false, &flags)) {
- return -EIO;
- }
- iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
- addr | (3 << 24));
- for (i = 0; i < size; i += 4)
- *(u32 *)(tst->mem.addr + i) =
- iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
- iwl_trans_release_nic_access(trans, &flags);
- } else { /* target memory (SRAM) */
- iwl_trans_read_mem(trans, addr, tst->mem.addr,
- tst->mem.size / 4);
- }
- tst->mem.nchunks =
- DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE);
- tst->mem.in_read = true;
- return 0;
- }
- /*
- * Handles indirect write to the periphery or SRAM. The is performed to a
- * temporary buffer.
- */
- static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
- u32 size, unsigned char *buf)
- {
- struct iwl_trans *trans = tst->trans;
- u32 val, i;
- unsigned long flags;
- if (IWL_ABS_PRPH_START <= addr &&
- addr < IWL_ABS_PRPH_START + PRPH_END) {
- /* Periphery writes can be 1-3 bytes long, or DWORDs */
- if (size < 4) {
- memcpy(&val, buf, size);
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- return -EIO;
- iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
- (addr & 0x0000FFFF) |
- ((size - 1) << 24));
- iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
- iwl_trans_release_nic_access(trans, &flags);
- } else {
- if (size % 4)
- return -EINVAL;
- for (i = 0; i < size; i += 4)
- iwl_write_prph(trans, addr+i,
- *(u32 *)(buf+i));
- }
- } else if (iwl_test_valid_hw_addr(tst, addr)) {
- iwl_trans_write_mem(trans, addr, buf, size / 4);
- } else {
- return -EINVAL;
- }
- return 0;
- }
- /*
- * Handles the user application commands for indirect read/write
- * to/from the periphery or the SRAM.
- */
- static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb)
- {
- u32 addr, size, cmd;
- unsigned char *buf;
- /* Both read and write should be blocked, for atomicity */
- if (tst->mem.in_read)
- return -EBUSY;
- cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
- if (!tb[IWL_TM_ATTR_MEM_ADDR]) {
- IWL_ERR(tst->trans, "Error finding memory offset address\n");
- return -ENOMSG;
- }
- addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]);
- if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) {
- IWL_ERR(tst->trans, "Error finding size for memory reading\n");
- return -ENOMSG;
- }
- size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]);
- if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) {
- return iwl_test_indirect_read(tst, addr, size);
- } else {
- if (!tb[IWL_TM_ATTR_BUFFER_DUMP])
- return -EINVAL;
- buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]);
- return iwl_test_indirect_write(tst, addr, size, buf);
- }
- }
- /*
- * Enable notifications to user space
- */
- static int iwl_test_notifications(struct iwl_test *tst,
- struct nlattr **tb)
- {
- tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]);
- return 0;
- }
- /*
- * Handles the request to get the device id
- */
- static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb)
- {
- u32 devid = tst->trans->hw_id;
- struct sk_buff *skb;
- int status;
- IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid);
- skb = iwl_test_alloc_reply(tst, 20);
- if (!skb) {
- IWL_ERR(tst->trans, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid))
- goto nla_put_failure;
- status = iwl_test_reply(tst, skb);
- if (status < 0)
- IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
- return 0;
- nla_put_failure:
- kfree_skb(skb);
- return -EMSGSIZE;
- }
- /*
- * Handles the request to get the FW version
- */
- static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb)
- {
- struct sk_buff *skb;
- int status;
- u32 ver = iwl_test_fw_ver(tst);
- IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver);
- skb = iwl_test_alloc_reply(tst, 20);
- if (!skb) {
- IWL_ERR(tst->trans, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver))
- goto nla_put_failure;
- status = iwl_test_reply(tst, skb);
- if (status < 0)
- IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
- return 0;
- nla_put_failure:
- kfree_skb(skb);
- return -EMSGSIZE;
- }
- /*
- * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists
- */
- int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
- void *data, int len)
- {
- int result;
- result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
- iwl_testmode_gnl_msg_policy);
- if (result) {
- IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result);
- return result;
- }
- /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
- if (!tb[IWL_TM_ATTR_COMMAND]) {
- IWL_ERR(tst->trans, "Missing testmode command type\n");
- return -ENOMSG;
- }
- return 0;
- }
- EXPORT_SYMBOL_GPL(iwl_test_parse);
- /*
- * Handle test commands.
- * Returns 1 for unknown commands (not handled by the test object); negative
- * value in case of error.
- */
- int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb)
- {
- int result;
- switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
- case IWL_TM_CMD_APP2DEV_UCODE:
- IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n");
- result = iwl_test_fw_cmd(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
- IWL_DEBUG_INFO(tst->trans, "test cmd to register\n");
- result = iwl_test_reg(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
- IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n");
- result = iwl_test_trace_begin(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_END_TRACE:
- iwl_test_trace_stop(tst);
- result = 0;
- break;
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
- IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n");
- result = iwl_test_indirect_mem(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
- IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n");
- result = iwl_test_notifications(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
- IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n");
- result = iwl_test_get_fw_ver(tst, tb);
- break;
- case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
- IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n");
- result = iwl_test_get_dev_id(tst, tb);
- break;
- default:
- IWL_DEBUG_INFO(tst->trans, "Unknown test command\n");
- result = 1;
- break;
- }
- return result;
- }
- EXPORT_SYMBOL_GPL(iwl_test_handle_cmd);
- static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb,
- struct netlink_callback *cb)
- {
- int idx, length;
- if (!tst->trace.enabled || !tst->trace.trace_addr)
- return -EFAULT;
- idx = cb->args[4];
- if (idx >= tst->trace.nchunks)
- return -ENOENT;
- length = DUMP_CHUNK_SIZE;
- if (((idx + 1) == tst->trace.nchunks) &&
- (tst->trace.size % DUMP_CHUNK_SIZE))
- length = tst->trace.size %
- DUMP_CHUNK_SIZE;
- if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length,
- tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx)))
- goto nla_put_failure;
- cb->args[4] = ++idx;
- return 0;
- nla_put_failure:
- return -ENOBUFS;
- }
- static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb,
- struct netlink_callback *cb)
- {
- int idx, length;
- if (!tst->mem.in_read)
- return -EFAULT;
- idx = cb->args[4];
- if (idx >= tst->mem.nchunks) {
- iwl_test_mem_stop(tst);
- return -ENOENT;
- }
- length = DUMP_CHUNK_SIZE;
- if (((idx + 1) == tst->mem.nchunks) &&
- (tst->mem.size % DUMP_CHUNK_SIZE))
- length = tst->mem.size % DUMP_CHUNK_SIZE;
- if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length,
- tst->mem.addr + (DUMP_CHUNK_SIZE * idx)))
- goto nla_put_failure;
- cb->args[4] = ++idx;
- return 0;
- nla_put_failure:
- return -ENOBUFS;
- }
- /*
- * Handle dump commands.
- * Returns 1 for unknown commands (not handled by the test object); negative
- * value in case of error.
- */
- int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
- struct netlink_callback *cb)
- {
- int result;
- switch (cmd) {
- case IWL_TM_CMD_APP2DEV_READ_TRACE:
- IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n");
- result = iwl_test_trace_dump(tst, skb, cb);
- break;
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP:
- IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n");
- result = iwl_test_buffer_dump(tst, skb, cb);
- break;
- default:
- result = 1;
- break;
- }
- return result;
- }
- EXPORT_SYMBOL_GPL(iwl_test_dump);
- /*
- * Multicast a spontaneous messages from the device to the user space.
- */
- static void iwl_test_send_rx(struct iwl_test *tst,
- struct iwl_rx_cmd_buffer *rxb)
- {
- struct sk_buff *skb;
- struct iwl_rx_packet *data;
- int length;
- data = rxb_addr(rxb);
- length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- /* the length doesn't include len_n_flags field, so add it manually */
- length += sizeof(__le32);
- skb = iwl_test_alloc_event(tst, length + 20);
- if (skb == NULL) {
- IWL_ERR(tst->trans, "Out of memory for message to user\n");
- return;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
- IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
- nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data))
- goto nla_put_failure;
- iwl_test_event(tst, skb);
- return;
- nla_put_failure:
- kfree_skb(skb);
- IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n");
- }
- /*
- * Called whenever a Rx frames is recevied from the device. If notifications to
- * the user space are requested, sends the frames to the user.
- */
- void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb)
- {
- if (tst->notify)
- iwl_test_send_rx(tst, rxb);
- }
- EXPORT_SYMBOL_GPL(iwl_test_rx);
|