2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
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.
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.
19 static void ath9k_tx99_stop(struct ath_softc
*sc
)
21 struct ath_hw
*ah
= sc
->sc_ah
;
22 struct ath_common
*common
= ath9k_hw_common(ah
);
24 ath_drain_all_txq(sc
);
27 ath9k_hw_set_interrupts(ah
);
28 ath9k_hw_enable_interrupts(ah
);
30 ieee80211_wake_queues(sc
->hw
);
32 kfree_skb(sc
->tx99_skb
);
34 sc
->tx99_state
= false;
36 ath9k_hw_tx99_stop(sc
->sc_ah
);
37 ath_dbg(common
, XMIT
, "TX99 stopped\n");
40 static struct sk_buff
*ath9k_build_tx99_skb(struct ath_softc
*sc
)
42 static u8 PN9Data
[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24,
43 0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50,
44 0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1,
45 0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18,
46 0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8,
47 0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84,
48 0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3,
49 0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0};
51 struct ieee80211_tx_rate
*rate
;
52 struct ieee80211_hw
*hw
= sc
->hw
;
53 struct ath_hw
*ah
= sc
->sc_ah
;
54 struct ieee80211_hdr
*hdr
;
55 struct ieee80211_tx_info
*tx_info
;
59 skb
= alloc_skb(len
, GFP_KERNEL
);
65 memset(skb
->data
, 0, len
);
67 hdr
= (struct ieee80211_hdr
*)skb
->data
;
68 hdr
->frame_control
= cpu_to_le16(IEEE80211_FTYPE_DATA
);
71 memcpy(hdr
->addr1
, hw
->wiphy
->perm_addr
, ETH_ALEN
);
72 memcpy(hdr
->addr2
, hw
->wiphy
->perm_addr
, ETH_ALEN
);
73 memcpy(hdr
->addr3
, hw
->wiphy
->perm_addr
, ETH_ALEN
);
76 avp
= (struct ath_vif
*) sc
->tx99_vif
->drv_priv
;
77 hdr
->seq_ctrl
|= cpu_to_le16(avp
->seq_no
);
80 tx_info
= IEEE80211_SKB_CB(skb
);
81 memset(tx_info
, 0, sizeof(*tx_info
));
82 rate
= &tx_info
->control
.rates
[0];
83 tx_info
->band
= sc
->cur_chan
->chandef
.chan
->band
;
84 tx_info
->flags
= IEEE80211_TX_CTL_NO_ACK
;
85 tx_info
->control
.vif
= sc
->tx99_vif
;
87 if (ah
->curchan
&& IS_CHAN_HT(ah
->curchan
)) {
88 rate
->flags
|= IEEE80211_TX_RC_MCS
;
89 if (IS_CHAN_HT40(ah
->curchan
))
90 rate
->flags
|= IEEE80211_TX_RC_40_MHZ_WIDTH
;
93 memcpy(skb
->data
+ sizeof(*hdr
), PN9Data
, sizeof(PN9Data
));
98 static void ath9k_tx99_deinit(struct ath_softc
*sc
)
104 ath9k_ps_restore(sc
);
107 static int ath9k_tx99_init(struct ath_softc
*sc
)
109 struct ieee80211_hw
*hw
= sc
->hw
;
110 struct ath_hw
*ah
= sc
->sc_ah
;
111 struct ath_common
*common
= ath9k_hw_common(ah
);
112 struct ath_tx_control txctl
;
115 if (test_bit(ATH_OP_INVALID
, &common
->op_flags
)) {
117 "driver is in invalid state unable to use TX99");
121 sc
->tx99_skb
= ath9k_build_tx99_skb(sc
);
125 memset(&txctl
, 0, sizeof(txctl
));
126 txctl
.txq
= sc
->tx
.txq_map
[IEEE80211_AC_VO
];
132 ath9k_hw_disable_interrupts(ah
);
133 ath_drain_all_txq(sc
);
136 sc
->tx99_state
= true;
138 ieee80211_stop_queues(hw
);
140 if (sc
->tx99_power
== MAX_RATE_POWER
+ 1)
141 sc
->tx99_power
= MAX_RATE_POWER
;
143 ath9k_hw_tx99_set_txpower(ah
, sc
->tx99_power
);
144 r
= ath9k_tx99_send(sc
, sc
->tx99_skb
, &txctl
);
146 ath_dbg(common
, XMIT
, "Failed to xmit TX99 skb\n");
150 ath_dbg(common
, XMIT
, "TX99 xmit started using %d ( %ddBm)\n",
154 /* We leave the hardware awake as it will be chugging on */
159 static ssize_t
read_file_tx99(struct file
*file
, char __user
*user_buf
,
160 size_t count
, loff_t
*ppos
)
162 struct ath_softc
*sc
= file
->private_data
;
166 len
= sprintf(buf
, "%d\n", sc
->tx99_state
);
167 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
170 static ssize_t
write_file_tx99(struct file
*file
, const char __user
*user_buf
,
171 size_t count
, loff_t
*ppos
)
173 struct ath_softc
*sc
= file
->private_data
;
174 struct ath_common
*common
= ath9k_hw_common(sc
->sc_ah
);
183 if (sc
->cur_chan
->nvifs
> 1)
186 len
= min(count
, sizeof(buf
) - 1);
187 if (copy_from_user(buf
, user_buf
, len
))
192 if (strtobool(buf
, &start
))
195 mutex_lock(&sc
->mutex
);
197 if (start
== sc
->tx99_state
) {
200 ath_dbg(common
, XMIT
, "Resetting TX99\n");
201 ath9k_tx99_deinit(sc
);
205 ath9k_tx99_deinit(sc
);
209 r
= ath9k_tx99_init(sc
);
211 mutex_unlock(&sc
->mutex
);
215 mutex_unlock(&sc
->mutex
);
219 static const struct file_operations fops_tx99
= {
220 .read
= read_file_tx99
,
221 .write
= write_file_tx99
,
223 .owner
= THIS_MODULE
,
224 .llseek
= default_llseek
,
227 static ssize_t
read_file_tx99_power(struct file
*file
,
228 char __user
*user_buf
,
229 size_t count
, loff_t
*ppos
)
231 struct ath_softc
*sc
= file
->private_data
;
235 len
= sprintf(buf
, "%d (%d dBm)\n",
239 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
242 static ssize_t
write_file_tx99_power(struct file
*file
,
243 const char __user
*user_buf
,
244 size_t count
, loff_t
*ppos
)
246 struct ath_softc
*sc
= file
->private_data
;
250 r
= kstrtou8_from_user(user_buf
, count
, 0, &tx_power
);
254 if (tx_power
> MAX_RATE_POWER
)
257 sc
->tx99_power
= tx_power
;
260 ath9k_hw_tx99_set_txpower(sc
->sc_ah
, sc
->tx99_power
);
261 ath9k_ps_restore(sc
);
266 static const struct file_operations fops_tx99_power
= {
267 .read
= read_file_tx99_power
,
268 .write
= write_file_tx99_power
,
270 .owner
= THIS_MODULE
,
271 .llseek
= default_llseek
,
274 void ath9k_tx99_init_debug(struct ath_softc
*sc
)
276 if (!AR_SREV_9280_20_OR_LATER(sc
->sc_ah
))
279 debugfs_create_file("tx99", 0600,
280 sc
->debug
.debugfs_phy
, sc
,
282 debugfs_create_file("tx99_power", 0600,
283 sc
->debug
.debugfs_phy
, sc
,