1 /* 2 * Copyright (c) 2010-2011 Atheros Communications 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 "testmode.h" 18 19 #include <net/netlink.h> 20 21 enum ath6kl_tm_attr { 22 __ATH6KL_TM_ATTR_INVALID = 0, 23 ATH6KL_TM_ATTR_CMD = 1, 24 ATH6KL_TM_ATTR_DATA = 2, 25 26 /* keep last */ 27 __ATH6KL_TM_ATTR_AFTER_LAST, 28 ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, 29 }; 30 31 enum ath6kl_tm_cmd { 32 ATH6KL_TM_CMD_TCMD = 0, 33 ATH6KL_TM_CMD_RX_REPORT = 1, 34 }; 35 36 #define ATH6KL_TM_DATA_MAX_LEN 5000 37 38 static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { 39 [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 }, 40 [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY, 41 .len = ATH6KL_TM_DATA_MAX_LEN }, 42 }; 43 44 void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len) 45 { 46 if (down_interruptible(&ar->sem)) 47 return; 48 49 kfree(ar->tm.rx_report); 50 51 ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL); 52 ar->tm.rx_report_len = buf_len; 53 54 up(&ar->sem); 55 56 wake_up(&ar->event_wq); 57 } 58 59 static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len, 60 struct sk_buff *skb) 61 { 62 int ret = 0; 63 long left; 64 65 if (down_interruptible(&ar->sem)) 66 return -ERESTARTSYS; 67 68 if (!test_bit(WMI_READY, &ar->flag)) { 69 ret = -EIO; 70 goto out; 71 } 72 73 if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { 74 ret = -EBUSY; 75 goto out; 76 } 77 78 if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) { 79 up(&ar->sem); 80 return -EIO; 81 } 82 83 left = wait_event_interruptible_timeout(ar->event_wq, 84 ar->tm.rx_report != NULL, 85 WMI_TIMEOUT); 86 87 if (left == 0) { 88 ret = -ETIMEDOUT; 89 goto out; 90 } else if (left < 0) { 91 ret = left; 92 goto out; 93 } 94 95 if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) { 96 ret = -EINVAL; 97 goto out; 98 } 99 100 NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len, 101 ar->tm.rx_report); 102 103 kfree(ar->tm.rx_report); 104 ar->tm.rx_report = NULL; 105 106 out: 107 up(&ar->sem); 108 109 return ret; 110 111 nla_put_failure: 112 ret = -ENOBUFS; 113 goto out; 114 } 115 116 int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) 117 { 118 struct ath6kl *ar = wiphy_priv(wiphy); 119 struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; 120 int err, buf_len, reply_len; 121 struct sk_buff *skb; 122 void *buf; 123 124 err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, 125 ath6kl_tm_policy); 126 if (err) 127 return err; 128 129 if (!tb[ATH6KL_TM_ATTR_CMD]) 130 return -EINVAL; 131 132 switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { 133 case ATH6KL_TM_CMD_TCMD: 134 if (!tb[ATH6KL_TM_ATTR_DATA]) 135 return -EINVAL; 136 137 buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); 138 buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); 139 140 ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); 141 142 return 0; 143 144 break; 145 case ATH6KL_TM_CMD_RX_REPORT: 146 if (!tb[ATH6KL_TM_ATTR_DATA]) 147 return -EINVAL; 148 149 buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); 150 buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); 151 152 reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN); 153 skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len); 154 if (!skb) 155 return -ENOMEM; 156 157 err = ath6kl_tm_rx_report(ar, buf, buf_len, skb); 158 if (err < 0) { 159 kfree_skb(skb); 160 return err; 161 } 162 163 return cfg80211_testmode_reply(skb); 164 default: 165 return -EOPNOTSUPP; 166 } 167 } 168