Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

staging: wfx: allow to send commands to chip

Chip has multiple input buffers and can handle multiple 802.11 frames
in parallel. However, other HIF command must be sent sequentially.
wsm_send_cmd() handles these requests.

This commit also add send_hif_cmd in debugfs. This file allows to send
arbitrary commands to chip. It can be used for debug and testing.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-12-Jerome.Pouiller@silabs.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Jérôme Pouiller and committed by
Greg Kroah-Hartman
4f8b7fab e16e7f07

+309 -1
+1
drivers/staging/wfx/Makefile
··· 7 7 bh.o \ 8 8 hwio.o \ 9 9 fwio.o \ 10 + hif_tx.o \ 10 11 hif_rx.o \ 11 12 main.o \ 12 13 sta.o \
+4 -1
drivers/staging/wfx/bh.c
··· 190 190 for (i = 0; i < max_msg; i++) { 191 191 hif = NULL; 192 192 if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) { 193 - /* FIXME: get queued data */ 193 + if (try_wait_for_completion(&wdev->hif_cmd.ready)) { 194 + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); 195 + hif = wdev->hif_cmd.buf_send; 196 + } 194 197 } 195 198 if (!hif) 196 199 return i;
+130
drivers/staging/wfx/debug.c
··· 7 7 */ 8 8 #include <linux/debugfs.h> 9 9 10 + #include "debug.h" 10 11 #include "wfx.h" 11 12 12 13 #define CREATE_TRACE_POINTS 13 14 #include "traces.h" 15 + 16 + static const struct trace_print_flags hif_msg_print_map[] = { 17 + hif_msg_list, 18 + }; 19 + 20 + static const struct trace_print_flags hif_mib_print_map[] = { 21 + hif_mib_list, 22 + }; 23 + 24 + static const struct trace_print_flags wfx_reg_print_map[] = { 25 + wfx_reg_list, 26 + }; 27 + 28 + static const char *get_symbol(unsigned long val, 29 + const struct trace_print_flags *symbol_array) 30 + { 31 + int i; 32 + 33 + for (i = 0; symbol_array[i].mask != -1; i++) { 34 + if (val == symbol_array[i].mask) 35 + return symbol_array[i].name; 36 + } 37 + 38 + return "unknown"; 39 + } 40 + 41 + const char *get_hif_name(unsigned long id) 42 + { 43 + return get_symbol(id, hif_msg_print_map); 44 + } 45 + 46 + const char *get_mib_name(unsigned long id) 47 + { 48 + return get_symbol(id, hif_mib_print_map); 49 + } 50 + 51 + const char *get_reg_name(unsigned long id) 52 + { 53 + return get_symbol(id, wfx_reg_print_map); 54 + } 55 + 56 + struct dbgfs_hif_msg { 57 + struct wfx_dev *wdev; 58 + struct completion complete; 59 + u8 reply[1024]; 60 + int ret; 61 + }; 62 + 63 + static ssize_t wfx_send_hif_msg_write(struct file *file, const char __user *user_buf, 64 + size_t count, loff_t *ppos) 65 + { 66 + struct dbgfs_hif_msg *context = file->private_data; 67 + struct wfx_dev *wdev = context->wdev; 68 + struct hif_msg *request; 69 + 70 + if (completion_done(&context->complete)) { 71 + dev_dbg(wdev->dev, "read previous result before start a new one\n"); 72 + return -EBUSY; 73 + } 74 + if (count < sizeof(struct hif_msg)) 75 + return -EINVAL; 76 + 77 + // wfx_cmd_send() chekc that reply buffer is wide enough, but do not 78 + // return precise length read. User have to know how many bytes should 79 + // be read. Filling reply buffer with a memory pattern may help user. 80 + memset(context->reply, sizeof(context->reply), 0xFF); 81 + request = memdup_user(user_buf, count); 82 + if (IS_ERR(request)) 83 + return PTR_ERR(request); 84 + if (request->len != count) { 85 + kfree(request); 86 + return -EINVAL; 87 + } 88 + context->ret = wfx_cmd_send(wdev, request, context->reply, sizeof(context->reply), false); 89 + 90 + kfree(request); 91 + complete(&context->complete); 92 + return count; 93 + } 94 + 95 + static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf, 96 + size_t count, loff_t *ppos) 97 + { 98 + struct dbgfs_hif_msg *context = file->private_data; 99 + int ret; 100 + 101 + if (count > sizeof(context->reply)) 102 + return -EINVAL; 103 + ret = wait_for_completion_interruptible(&context->complete); 104 + if (ret) 105 + return ret; 106 + if (context->ret < 0) 107 + return context->ret; 108 + // Be carefull, write() is waiting for a full message while read() 109 + // only return a payload 110 + ret = copy_to_user(user_buf, context->reply, count); 111 + if (ret) 112 + return ret; 113 + 114 + return count; 115 + } 116 + 117 + static int wfx_send_hif_msg_open(struct inode *inode, struct file *file) 118 + { 119 + struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL); 120 + 121 + if (!context) 122 + return -ENOMEM; 123 + context->wdev = inode->i_private; 124 + init_completion(&context->complete); 125 + file->private_data = context; 126 + return 0; 127 + } 128 + 129 + static int wfx_send_hif_msg_release(struct inode *inode, struct file *file) 130 + { 131 + struct dbgfs_hif_msg *context = file->private_data; 132 + 133 + kfree(context); 134 + return 0; 135 + } 136 + 137 + static const struct file_operations wfx_send_hif_msg_fops = { 138 + .open = wfx_send_hif_msg_open, 139 + .release = wfx_send_hif_msg_release, 140 + .write = wfx_send_hif_msg_write, 141 + .read = wfx_send_hif_msg_read, 142 + }; 14 143 15 144 int wfx_debug_init(struct wfx_dev *wdev) 16 145 { 17 146 struct dentry *d; 18 147 19 148 d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); 149 + debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); 20 150 21 151 return 0; 22 152 }
+4
drivers/staging/wfx/debug.h
··· 12 12 13 13 int wfx_debug_init(struct wfx_dev *wdev); 14 14 15 + const char *get_hif_name(unsigned long id); 16 + const char *get_mib_name(unsigned long id); 17 + const char *get_reg_name(unsigned long id); 18 + 15 19 #endif /* WFX_DEBUG_H */
+45
drivers/staging/wfx/hif_rx.c
··· 13 13 #include "wfx.h" 14 14 #include "hif_api_cmd.h" 15 15 16 + static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) 17 + { 18 + // All confirm messages start with status 19 + int status = le32_to_cpu(*((__le32 *) buf)); 20 + int cmd = hif->id; 21 + int len = hif->len - 4; // drop header 22 + 23 + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); 24 + 25 + if (!wdev->hif_cmd.buf_send) { 26 + dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd); 27 + return -EINVAL; 28 + } 29 + 30 + if (cmd != wdev->hif_cmd.buf_send->id) { 31 + dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n", 32 + cmd, wdev->hif_cmd.buf_send->id); 33 + return -EINVAL; 34 + } 35 + 36 + if (wdev->hif_cmd.buf_recv) { 37 + if (wdev->hif_cmd.len_recv >= len) 38 + memcpy(wdev->hif_cmd.buf_recv, buf, len); 39 + else 40 + status = -ENOMEM; 41 + } 42 + wdev->hif_cmd.ret = status; 43 + 44 + if (!wdev->hif_cmd.async) { 45 + complete(&wdev->hif_cmd.done); 46 + } else { 47 + wdev->hif_cmd.buf_send = NULL; 48 + mutex_unlock(&wdev->hif_cmd.lock); 49 + } 50 + return status; 51 + } 52 + 16 53 static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) 17 54 { 18 55 struct hif_ind_startup *body = buf; ··· 81 44 struct hif_msg *hif = (struct hif_msg *) skb->data; 82 45 int hif_id = hif->id; 83 46 47 + // Note: mutex_is_lock cause an implicit memory barrier that protect 48 + // buf_send 49 + if (mutex_is_locked(&wdev->hif_cmd.lock) 50 + && wdev->hif_cmd.buf_send 51 + && wdev->hif_cmd.buf_send->id == hif_id) { 52 + hif_generic_confirm(wdev, hif, hif->body); 53 + goto free; 54 + } 84 55 for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) { 85 56 if (hif_handlers[i].msg_id == hif_id) { 86 57 if (hif_handlers[i].handler)
+87
drivers/staging/wfx/hif_tx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx 4 + * Split Mac (WSM) API. 5 + * 6 + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. 7 + * Copyright (c) 2010, ST-Ericsson 8 + */ 9 + #include <linux/skbuff.h> 10 + #include <linux/etherdevice.h> 11 + 12 + #include "hif_tx.h" 13 + #include "wfx.h" 14 + #include "bh.h" 15 + #include "debug.h" 16 + 17 + void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) 18 + { 19 + init_completion(&hif_cmd->ready); 20 + init_completion(&hif_cmd->done); 21 + mutex_init(&hif_cmd->lock); 22 + } 23 + 24 + int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async) 25 + { 26 + const char *mib_name = ""; 27 + const char *mib_sep = ""; 28 + int cmd = request->id; 29 + int vif = request->interface; 30 + int ret; 31 + 32 + WARN(wdev->hif_cmd.buf_recv && wdev->hif_cmd.async, "API usage error"); 33 + 34 + // Do not wait for any reply if chip is frozen 35 + if (wdev->chip_frozen) 36 + return -ETIMEDOUT; 37 + 38 + mutex_lock(&wdev->hif_cmd.lock); 39 + WARN(wdev->hif_cmd.buf_send, "data locking error"); 40 + 41 + // Note: call to complete() below has an implicit memory barrier that 42 + // hopefully protect buf_send 43 + wdev->hif_cmd.buf_send = request; 44 + wdev->hif_cmd.buf_recv = reply; 45 + wdev->hif_cmd.len_recv = reply_len; 46 + wdev->hif_cmd.async = async; 47 + complete(&wdev->hif_cmd.ready); 48 + 49 + wfx_bh_request_tx(wdev); 50 + 51 + // NOTE: no timeout is catched async is enabled 52 + if (async) 53 + return 0; 54 + 55 + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ); 56 + if (!ret) { 57 + dev_err(wdev->dev, "chip is abnormally long to answer\n"); 58 + reinit_completion(&wdev->hif_cmd.ready); 59 + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ); 60 + } 61 + if (!ret) { 62 + dev_err(wdev->dev, "chip did not answer\n"); 63 + wdev->chip_frozen = 1; 64 + reinit_completion(&wdev->hif_cmd.done); 65 + ret = -ETIMEDOUT; 66 + } else { 67 + ret = wdev->hif_cmd.ret; 68 + } 69 + 70 + wdev->hif_cmd.buf_send = NULL; 71 + mutex_unlock(&wdev->hif_cmd.lock); 72 + 73 + if (ret && (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) { 74 + mib_name = get_mib_name(((u16 *) request)[2]); 75 + mib_sep = "/"; 76 + } 77 + if (ret < 0) 78 + dev_err(wdev->dev, 79 + "WSM request %s%s%s (%#.2x) on vif %d returned error %d\n", 80 + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); 81 + if (ret > 0) 82 + dev_warn(wdev->dev, 83 + "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n", 84 + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); 85 + 86 + return ret; 87 + }
+33
drivers/staging/wfx/hif_tx.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx 4 + * Split Mac (WSM) API. 5 + * 6 + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. 7 + * Copyright (c) 2010, ST-Ericsson 8 + * Copyright (C) 2010, ST-Ericsson SA 9 + */ 10 + #ifndef WFX_HIF_TX_H 11 + #define WFX_HIF_TX_H 12 + 13 + #include "hif_api_cmd.h" 14 + 15 + struct wfx_dev; 16 + struct wfx_vif; 17 + 18 + struct wfx_hif_cmd { 19 + struct mutex lock; 20 + struct completion ready; 21 + struct completion done; 22 + bool async; 23 + struct hif_msg *buf_send; 24 + void *buf_recv; 25 + size_t len_recv; 26 + int ret; 27 + }; 28 + 29 + void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd); 30 + int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, 31 + void *reply, size_t reply_len, bool async); 32 + 33 + #endif
+1
drivers/staging/wfx/main.c
··· 115 115 memcpy(&wdev->pdata, pdata, sizeof(*pdata)); 116 116 117 117 init_completion(&wdev->firmware_ready); 118 + wfx_init_hif_cmd(&wdev->hif_cmd); 118 119 119 120 return wdev; 120 121 }
+4
drivers/staging/wfx/wfx.h
··· 15 15 16 16 #include "bh.h" 17 17 #include "main.h" 18 + #include "hif_tx.h" 18 19 #include "hif_api_general.h" 19 20 20 21 struct hwbus_ops; ··· 33 32 struct completion firmware_ready; 34 33 struct hif_ind_startup hw_caps; 35 34 struct wfx_hif hif; 35 + int chip_frozen; 36 + 37 + struct wfx_hif_cmd hif_cmd; 36 38 }; 37 39 38 40 struct wfx_vif {