1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * cfg80211 debugfs 4 * 5 * Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com> 6 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 7 * Copyright (C) 2023 Intel Corporation 8 */ 9 10 #include <linux/slab.h> 11 #include "core.h" 12 #include "debugfs.h" 13 14 #define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ 15 static ssize_t name## _read(struct file *file, char __user *userbuf, \ 16 size_t count, loff_t *ppos) \ 17 { \ 18 struct wiphy *wiphy = file->private_data; \ 19 char buf[buflen]; \ 20 int res; \ 21 \ 22 res = scnprintf(buf, buflen, fmt "\n", ##value); \ 23 return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ 24 } \ 25 \ 26 static const struct file_operations name## _ops = { \ 27 .owner = THIS_MODULE, \ 28 .read = name## _read, \ 29 .open = simple_open, \ 30 .llseek = generic_file_llseek, \ 31 } 32 33 #define DEBUGFS_RADIO_READONLY_FILE(name, buflen, fmt, value...) \ 34 static ssize_t name## _read(struct file *file, char __user *userbuf, \ 35 size_t count, loff_t *ppos) \ 36 { \ 37 struct wiphy_radio_cfg *radio_cfg = file->private_data; \ 38 char buf[buflen]; \ 39 int res; \ 40 \ 41 res = scnprintf(buf, buflen, fmt "\n", ##value); \ 42 return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ 43 } \ 44 \ 45 static const struct file_operations name## _ops = { \ 46 .owner = THIS_MODULE, \ 47 .read = name## _read, \ 48 .open = simple_open, \ 49 .llseek = generic_file_llseek, \ 50 } 51 52 DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", 53 wiphy->rts_threshold); 54 DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", 55 wiphy->frag_threshold); 56 DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", 57 wiphy->retry_short); 58 DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", 59 wiphy->retry_long); 60 61 DEBUGFS_RADIO_READONLY_FILE(radio_rts_threshold, 20, "%d", 62 radio_cfg->rts_threshold); 63 64 static int ht_print_chan(struct ieee80211_channel *chan, 65 char *buf, int buf_size, int offset) 66 { 67 if (WARN_ON(offset > buf_size)) 68 return 0; 69 70 if (chan->flags & IEEE80211_CHAN_DISABLED) 71 return scnprintf(buf + offset, 72 buf_size - offset, 73 "%d Disabled\n", 74 chan->center_freq); 75 76 return scnprintf(buf + offset, 77 buf_size - offset, 78 "%d HT40 %c%c\n", 79 chan->center_freq, 80 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? 81 ' ' : '-', 82 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? 83 ' ' : '+'); 84 } 85 86 static ssize_t ht40allow_map_read(struct file *file, 87 char __user *user_buf, 88 size_t count, loff_t *ppos) 89 { 90 struct wiphy *wiphy = file->private_data; 91 char *buf; 92 unsigned int offset = 0, buf_size = PAGE_SIZE, i; 93 enum nl80211_band band; 94 struct ieee80211_supported_band *sband; 95 ssize_t r; 96 97 buf = kzalloc(buf_size, GFP_KERNEL); 98 if (!buf) 99 return -ENOMEM; 100 101 for (band = 0; band < NUM_NL80211_BANDS; band++) { 102 sband = wiphy->bands[band]; 103 if (!sband) 104 continue; 105 for (i = 0; i < sband->n_channels; i++) 106 offset += ht_print_chan(&sband->channels[i], 107 buf, buf_size, offset); 108 } 109 110 r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 111 112 kfree(buf); 113 114 return r; 115 } 116 117 static const struct file_operations ht40allow_map_ops = { 118 .owner = THIS_MODULE, 119 .read = ht40allow_map_read, 120 .open = simple_open, 121 .llseek = default_llseek, 122 }; 123 124 #define DEBUGFS_ADD(name) \ 125 debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops) 126 127 #define DEBUGFS_RADIO_ADD(name, radio_idx) \ 128 debugfs_create_file(#name, 0444, radiod, \ 129 &rdev->wiphy.radio_cfg[radio_idx], \ 130 &name## _ops) 131 132 void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) 133 { 134 struct dentry *phyd = rdev->wiphy.debugfsdir; 135 struct dentry *radiod; 136 u8 i; 137 138 DEBUGFS_ADD(rts_threshold); 139 DEBUGFS_ADD(fragmentation_threshold); 140 DEBUGFS_ADD(short_retry_limit); 141 DEBUGFS_ADD(long_retry_limit); 142 DEBUGFS_ADD(ht40allow_map); 143 144 for (i = 0; i < rdev->wiphy.n_radio; i++) { 145 radiod = rdev->wiphy.radio_cfg[i].radio_debugfsdir; 146 DEBUGFS_RADIO_ADD(radio_rts_threshold, i); 147 } 148 } 149 150 struct debugfs_read_work { 151 struct wiphy_work work; 152 ssize_t (*handler)(struct wiphy *wiphy, 153 struct file *file, 154 char *buf, 155 size_t count, 156 void *data); 157 struct wiphy *wiphy; 158 struct file *file; 159 char *buf; 160 size_t bufsize; 161 void *data; 162 ssize_t ret; 163 struct completion completion; 164 }; 165 166 static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy, 167 struct wiphy_work *work) 168 { 169 struct debugfs_read_work *w = container_of(work, typeof(*w), work); 170 171 w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data); 172 complete(&w->completion); 173 } 174 175 static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry, 176 void *data) 177 { 178 struct debugfs_read_work *w = data; 179 180 wiphy_work_cancel(w->wiphy, &w->work); 181 complete(&w->completion); 182 } 183 184 ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file, 185 char *buf, size_t bufsize, 186 char __user *userbuf, size_t count, 187 loff_t *ppos, 188 ssize_t (*handler)(struct wiphy *wiphy, 189 struct file *file, 190 char *buf, 191 size_t bufsize, 192 void *data), 193 void *data) 194 { 195 struct debugfs_read_work work = { 196 .handler = handler, 197 .wiphy = wiphy, 198 .file = file, 199 .buf = buf, 200 .bufsize = bufsize, 201 .data = data, 202 .ret = -ENODEV, 203 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion), 204 }; 205 struct debugfs_cancellation cancellation = { 206 .cancel = wiphy_locked_debugfs_read_cancel, 207 .cancel_data = &work, 208 }; 209 210 /* don't leak stack data or whatever */ 211 memset(buf, 0, bufsize); 212 213 wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work); 214 wiphy_work_queue(wiphy, &work.work); 215 216 debugfs_enter_cancellation(file, &cancellation); 217 wait_for_completion(&work.completion); 218 debugfs_leave_cancellation(file, &cancellation); 219 220 if (work.ret < 0) 221 return work.ret; 222 223 if (WARN_ON(work.ret > bufsize)) 224 return -EINVAL; 225 226 return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret); 227 } 228 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read); 229 230 struct debugfs_write_work { 231 struct wiphy_work work; 232 ssize_t (*handler)(struct wiphy *wiphy, 233 struct file *file, 234 char *buf, 235 size_t count, 236 void *data); 237 struct wiphy *wiphy; 238 struct file *file; 239 char *buf; 240 size_t count; 241 void *data; 242 ssize_t ret; 243 struct completion completion; 244 }; 245 246 static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy, 247 struct wiphy_work *work) 248 { 249 struct debugfs_write_work *w = container_of(work, typeof(*w), work); 250 251 w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data); 252 complete(&w->completion); 253 } 254 255 static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry, 256 void *data) 257 { 258 struct debugfs_write_work *w = data; 259 260 wiphy_work_cancel(w->wiphy, &w->work); 261 complete(&w->completion); 262 } 263 264 ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy, 265 struct file *file, char *buf, size_t bufsize, 266 const char __user *userbuf, size_t count, 267 ssize_t (*handler)(struct wiphy *wiphy, 268 struct file *file, 269 char *buf, 270 size_t count, 271 void *data), 272 void *data) 273 { 274 struct debugfs_write_work work = { 275 .handler = handler, 276 .wiphy = wiphy, 277 .file = file, 278 .buf = buf, 279 .count = count, 280 .data = data, 281 .ret = -ENODEV, 282 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion), 283 }; 284 struct debugfs_cancellation cancellation = { 285 .cancel = wiphy_locked_debugfs_write_cancel, 286 .cancel_data = &work, 287 }; 288 289 /* mostly used for strings so enforce NUL-termination for safety */ 290 if (count >= bufsize) 291 return -EINVAL; 292 293 memset(buf, 0, bufsize); 294 295 if (copy_from_user(buf, userbuf, count)) 296 return -EFAULT; 297 298 wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work); 299 wiphy_work_queue(wiphy, &work.work); 300 301 debugfs_enter_cancellation(file, &cancellation); 302 wait_for_completion(&work.completion); 303 debugfs_leave_cancellation(file, &cancellation); 304 305 return work.ret; 306 } 307 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write); 308