| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * mac80211 debugfs for wireless PHYs |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
|---|
| 5 | 6 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
|---|
| 6 | | - * |
|---|
| 7 | | - * GPLv2 |
|---|
| 8 | | - * |
|---|
| 7 | + * Copyright (C) 2018 - 2019 Intel Corporation |
|---|
| 9 | 8 | */ |
|---|
| 10 | 9 | |
|---|
| 11 | 10 | #include <linux/debugfs.h> |
|---|
| .. | .. |
|---|
| 60 | 59 | debugfs_create_file(#name, mode, phyd, local, &name## _ops); |
|---|
| 61 | 60 | |
|---|
| 62 | 61 | |
|---|
| 62 | +DEBUGFS_READONLY_FILE(hw_conf, "%x", |
|---|
| 63 | + local->hw.conf.flags); |
|---|
| 63 | 64 | DEBUGFS_READONLY_FILE(user_power, "%d", |
|---|
| 64 | 65 | local->user_power_level); |
|---|
| 65 | 66 | DEBUGFS_READONLY_FILE(power, "%d", |
|---|
| .. | .. |
|---|
| 119 | 120 | { |
|---|
| 120 | 121 | struct ieee80211_local *local = file->private_data; |
|---|
| 121 | 122 | char buf[100]; |
|---|
| 122 | | - size_t len; |
|---|
| 123 | 123 | |
|---|
| 124 | | - if (count > sizeof(buf)) |
|---|
| 124 | + if (count >= sizeof(buf)) |
|---|
| 125 | 125 | return -EINVAL; |
|---|
| 126 | 126 | |
|---|
| 127 | 127 | if (copy_from_user(buf, user_buf, count)) |
|---|
| 128 | 128 | return -EFAULT; |
|---|
| 129 | 129 | |
|---|
| 130 | | - buf[sizeof(buf) - 1] = '\0'; |
|---|
| 131 | | - len = strlen(buf); |
|---|
| 132 | | - if (len > 0 && buf[len-1] == '\n') |
|---|
| 133 | | - buf[len-1] = 0; |
|---|
| 130 | + if (count && buf[count - 1] == '\n') |
|---|
| 131 | + buf[count - 1] = '\0'; |
|---|
| 132 | + else |
|---|
| 133 | + buf[count] = '\0'; |
|---|
| 134 | 134 | |
|---|
| 135 | 135 | if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) |
|---|
| 136 | 136 | return count; |
|---|
| .. | .. |
|---|
| 145 | 145 | static const struct file_operations aqm_ops = { |
|---|
| 146 | 146 | .write = aqm_write, |
|---|
| 147 | 147 | .read = aqm_read, |
|---|
| 148 | + .open = simple_open, |
|---|
| 149 | + .llseek = default_llseek, |
|---|
| 150 | +}; |
|---|
| 151 | + |
|---|
| 152 | +static ssize_t airtime_flags_read(struct file *file, |
|---|
| 153 | + char __user *user_buf, |
|---|
| 154 | + size_t count, loff_t *ppos) |
|---|
| 155 | +{ |
|---|
| 156 | + struct ieee80211_local *local = file->private_data; |
|---|
| 157 | + char buf[128] = {}, *pos, *end; |
|---|
| 158 | + |
|---|
| 159 | + pos = buf; |
|---|
| 160 | + end = pos + sizeof(buf) - 1; |
|---|
| 161 | + |
|---|
| 162 | + if (local->airtime_flags & AIRTIME_USE_TX) |
|---|
| 163 | + pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n", |
|---|
| 164 | + AIRTIME_USE_TX); |
|---|
| 165 | + if (local->airtime_flags & AIRTIME_USE_RX) |
|---|
| 166 | + pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n", |
|---|
| 167 | + AIRTIME_USE_RX); |
|---|
| 168 | + |
|---|
| 169 | + return simple_read_from_buffer(user_buf, count, ppos, buf, |
|---|
| 170 | + strlen(buf)); |
|---|
| 171 | +} |
|---|
| 172 | + |
|---|
| 173 | +static ssize_t airtime_flags_write(struct file *file, |
|---|
| 174 | + const char __user *user_buf, |
|---|
| 175 | + size_t count, loff_t *ppos) |
|---|
| 176 | +{ |
|---|
| 177 | + struct ieee80211_local *local = file->private_data; |
|---|
| 178 | + char buf[16]; |
|---|
| 179 | + |
|---|
| 180 | + if (count >= sizeof(buf)) |
|---|
| 181 | + return -EINVAL; |
|---|
| 182 | + |
|---|
| 183 | + if (copy_from_user(buf, user_buf, count)) |
|---|
| 184 | + return -EFAULT; |
|---|
| 185 | + |
|---|
| 186 | + if (count && buf[count - 1] == '\n') |
|---|
| 187 | + buf[count - 1] = '\0'; |
|---|
| 188 | + else |
|---|
| 189 | + buf[count] = '\0'; |
|---|
| 190 | + |
|---|
| 191 | + if (kstrtou16(buf, 0, &local->airtime_flags)) |
|---|
| 192 | + return -EINVAL; |
|---|
| 193 | + |
|---|
| 194 | + return count; |
|---|
| 195 | +} |
|---|
| 196 | + |
|---|
| 197 | +static const struct file_operations airtime_flags_ops = { |
|---|
| 198 | + .write = airtime_flags_write, |
|---|
| 199 | + .read = airtime_flags_read, |
|---|
| 200 | + .open = simple_open, |
|---|
| 201 | + .llseek = default_llseek, |
|---|
| 202 | +}; |
|---|
| 203 | + |
|---|
| 204 | +static ssize_t aql_txq_limit_read(struct file *file, |
|---|
| 205 | + char __user *user_buf, |
|---|
| 206 | + size_t count, |
|---|
| 207 | + loff_t *ppos) |
|---|
| 208 | +{ |
|---|
| 209 | + struct ieee80211_local *local = file->private_data; |
|---|
| 210 | + char buf[400]; |
|---|
| 211 | + int len = 0; |
|---|
| 212 | + |
|---|
| 213 | + len = scnprintf(buf, sizeof(buf), |
|---|
| 214 | + "AC AQL limit low AQL limit high\n" |
|---|
| 215 | + "VO %u %u\n" |
|---|
| 216 | + "VI %u %u\n" |
|---|
| 217 | + "BE %u %u\n" |
|---|
| 218 | + "BK %u %u\n", |
|---|
| 219 | + local->aql_txq_limit_low[IEEE80211_AC_VO], |
|---|
| 220 | + local->aql_txq_limit_high[IEEE80211_AC_VO], |
|---|
| 221 | + local->aql_txq_limit_low[IEEE80211_AC_VI], |
|---|
| 222 | + local->aql_txq_limit_high[IEEE80211_AC_VI], |
|---|
| 223 | + local->aql_txq_limit_low[IEEE80211_AC_BE], |
|---|
| 224 | + local->aql_txq_limit_high[IEEE80211_AC_BE], |
|---|
| 225 | + local->aql_txq_limit_low[IEEE80211_AC_BK], |
|---|
| 226 | + local->aql_txq_limit_high[IEEE80211_AC_BK]); |
|---|
| 227 | + return simple_read_from_buffer(user_buf, count, ppos, |
|---|
| 228 | + buf, len); |
|---|
| 229 | +} |
|---|
| 230 | + |
|---|
| 231 | +static ssize_t aql_txq_limit_write(struct file *file, |
|---|
| 232 | + const char __user *user_buf, |
|---|
| 233 | + size_t count, |
|---|
| 234 | + loff_t *ppos) |
|---|
| 235 | +{ |
|---|
| 236 | + struct ieee80211_local *local = file->private_data; |
|---|
| 237 | + char buf[100]; |
|---|
| 238 | + u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; |
|---|
| 239 | + struct sta_info *sta; |
|---|
| 240 | + |
|---|
| 241 | + if (count >= sizeof(buf)) |
|---|
| 242 | + return -EINVAL; |
|---|
| 243 | + |
|---|
| 244 | + if (copy_from_user(buf, user_buf, count)) |
|---|
| 245 | + return -EFAULT; |
|---|
| 246 | + |
|---|
| 247 | + if (count && buf[count - 1] == '\n') |
|---|
| 248 | + buf[count - 1] = '\0'; |
|---|
| 249 | + else |
|---|
| 250 | + buf[count] = '\0'; |
|---|
| 251 | + |
|---|
| 252 | + if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) |
|---|
| 253 | + return -EINVAL; |
|---|
| 254 | + |
|---|
| 255 | + if (ac >= IEEE80211_NUM_ACS) |
|---|
| 256 | + return -EINVAL; |
|---|
| 257 | + |
|---|
| 258 | + q_limit_low_old = local->aql_txq_limit_low[ac]; |
|---|
| 259 | + q_limit_high_old = local->aql_txq_limit_high[ac]; |
|---|
| 260 | + |
|---|
| 261 | + local->aql_txq_limit_low[ac] = q_limit_low; |
|---|
| 262 | + local->aql_txq_limit_high[ac] = q_limit_high; |
|---|
| 263 | + |
|---|
| 264 | + mutex_lock(&local->sta_mtx); |
|---|
| 265 | + list_for_each_entry(sta, &local->sta_list, list) { |
|---|
| 266 | + /* If a sta has customized queue limits, keep it */ |
|---|
| 267 | + if (sta->airtime[ac].aql_limit_low == q_limit_low_old && |
|---|
| 268 | + sta->airtime[ac].aql_limit_high == q_limit_high_old) { |
|---|
| 269 | + sta->airtime[ac].aql_limit_low = q_limit_low; |
|---|
| 270 | + sta->airtime[ac].aql_limit_high = q_limit_high; |
|---|
| 271 | + } |
|---|
| 272 | + } |
|---|
| 273 | + mutex_unlock(&local->sta_mtx); |
|---|
| 274 | + return count; |
|---|
| 275 | +} |
|---|
| 276 | + |
|---|
| 277 | +static const struct file_operations aql_txq_limit_ops = { |
|---|
| 278 | + .write = aql_txq_limit_write, |
|---|
| 279 | + .read = aql_txq_limit_read, |
|---|
| 280 | + .open = simple_open, |
|---|
| 281 | + .llseek = default_llseek, |
|---|
| 282 | +}; |
|---|
| 283 | + |
|---|
| 284 | +static ssize_t force_tx_status_read(struct file *file, |
|---|
| 285 | + char __user *user_buf, |
|---|
| 286 | + size_t count, |
|---|
| 287 | + loff_t *ppos) |
|---|
| 288 | +{ |
|---|
| 289 | + struct ieee80211_local *local = file->private_data; |
|---|
| 290 | + char buf[3]; |
|---|
| 291 | + int len = 0; |
|---|
| 292 | + |
|---|
| 293 | + len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status); |
|---|
| 294 | + |
|---|
| 295 | + return simple_read_from_buffer(user_buf, count, ppos, |
|---|
| 296 | + buf, len); |
|---|
| 297 | +} |
|---|
| 298 | + |
|---|
| 299 | +static ssize_t force_tx_status_write(struct file *file, |
|---|
| 300 | + const char __user *user_buf, |
|---|
| 301 | + size_t count, |
|---|
| 302 | + loff_t *ppos) |
|---|
| 303 | +{ |
|---|
| 304 | + struct ieee80211_local *local = file->private_data; |
|---|
| 305 | + char buf[3]; |
|---|
| 306 | + |
|---|
| 307 | + if (count >= sizeof(buf)) |
|---|
| 308 | + return -EINVAL; |
|---|
| 309 | + |
|---|
| 310 | + if (copy_from_user(buf, user_buf, count)) |
|---|
| 311 | + return -EFAULT; |
|---|
| 312 | + |
|---|
| 313 | + if (count && buf[count - 1] == '\n') |
|---|
| 314 | + buf[count - 1] = '\0'; |
|---|
| 315 | + else |
|---|
| 316 | + buf[count] = '\0'; |
|---|
| 317 | + |
|---|
| 318 | + if (buf[0] == '0' && buf[1] == '\0') |
|---|
| 319 | + local->force_tx_status = 0; |
|---|
| 320 | + else if (buf[0] == '1' && buf[1] == '\0') |
|---|
| 321 | + local->force_tx_status = 1; |
|---|
| 322 | + else |
|---|
| 323 | + return -EINVAL; |
|---|
| 324 | + |
|---|
| 325 | + return count; |
|---|
| 326 | +} |
|---|
| 327 | + |
|---|
| 328 | +static const struct file_operations force_tx_status_ops = { |
|---|
| 329 | + .write = force_tx_status_write, |
|---|
| 330 | + .read = force_tx_status_read, |
|---|
| 148 | 331 | .open = simple_open, |
|---|
| 149 | 332 | .llseek = default_llseek, |
|---|
| 150 | 333 | }; |
|---|
| .. | .. |
|---|
| 214 | 397 | FLAG(SUPPORTS_TDLS_BUFFER_STA), |
|---|
| 215 | 398 | FLAG(DEAUTH_NEED_MGD_TX_PREP), |
|---|
| 216 | 399 | FLAG(DOESNT_SUPPORT_QOS_NDP), |
|---|
| 400 | + FLAG(BUFF_MMPDU_TXQ), |
|---|
| 401 | + FLAG(SUPPORTS_VHT_EXT_NSS_BW), |
|---|
| 402 | + FLAG(STA_MMPDU_TXQ), |
|---|
| 403 | + FLAG(TX_STATUS_NO_AMPDU_LEN), |
|---|
| 404 | + FLAG(SUPPORTS_MULTI_BSSID), |
|---|
| 405 | + FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), |
|---|
| 406 | + FLAG(AMPDU_KEYBORDER_SUPPORT), |
|---|
| 407 | + FLAG(SUPPORTS_TX_ENCAP_OFFLOAD), |
|---|
| 217 | 408 | #undef FLAG |
|---|
| 218 | 409 | }; |
|---|
| 219 | 410 | |
|---|
| .. | .. |
|---|
| 375 | 566 | DEBUGFS_ADD(hwflags); |
|---|
| 376 | 567 | DEBUGFS_ADD(user_power); |
|---|
| 377 | 568 | DEBUGFS_ADD(power); |
|---|
| 569 | + DEBUGFS_ADD(hw_conf); |
|---|
| 570 | + DEBUGFS_ADD_MODE(force_tx_status, 0600); |
|---|
| 378 | 571 | |
|---|
| 379 | 572 | if (local->ops->wake_tx_queue) |
|---|
| 380 | 573 | DEBUGFS_ADD_MODE(aqm, 0600); |
|---|
| 381 | 574 | |
|---|
| 575 | + DEBUGFS_ADD_MODE(airtime_flags, 0600); |
|---|
| 576 | + |
|---|
| 577 | + DEBUGFS_ADD(aql_txq_limit); |
|---|
| 578 | + debugfs_create_u32("aql_threshold", 0600, |
|---|
| 579 | + phyd, &local->aql_threshold); |
|---|
| 580 | + |
|---|
| 382 | 581 | statsd = debugfs_create_dir("statistics", phyd); |
|---|
| 383 | 582 | |
|---|
| 384 | 583 | /* if the dir failed, don't put all the other things into the root! */ |
|---|