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 .read = name## _read, \ 28 .open = simple_open, \ 29 .llseek = generic_file_llseek, \ 30 } 31 32 DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", 33 wiphy->rts_threshold); 34 DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", 35 wiphy->frag_threshold); 36 DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", 37 wiphy->retry_short); 38 DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", 39 wiphy->retry_long); 40 41 static int ht_print_chan(struct ieee80211_channel *chan, 42 char *buf, int buf_size, int offset) 43 { 44 if (WARN_ON(offset > buf_size)) 45 return 0; 46 47 if (chan->flags & IEEE80211_CHAN_DISABLED) 48 return scnprintf(buf + offset, 49 buf_size - offset, 50 "%d Disabled\n", 51 chan->center_freq); 52 53 return scnprintf(buf + offset, 54 buf_size - offset, 55 "%d HT40 %c%c\n", 56 chan->center_freq, 57 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? 58 ' ' : '-', 59 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? 60 ' ' : '+'); 61 } 62 63 static ssize_t ht40allow_map_read(struct file *file, 64 char __user *user_buf, 65 size_t count, loff_t *ppos) 66 { 67 struct wiphy *wiphy = file->private_data; 68 char *buf; 69 unsigned int offset = 0, buf_size = PAGE_SIZE, i; 70 enum nl80211_band band; 71 struct ieee80211_supported_band *sband; 72 ssize_t r; 73 74 buf = kzalloc(buf_size, GFP_KERNEL); 75 if (!buf) 76 return -ENOMEM; 77 78 for (band = 0; band < NUM_NL80211_BANDS; band++) { 79 sband = wiphy->bands[band]; 80 if (!sband) 81 continue; 82 for (i = 0; i < sband->n_channels; i++) 83 offset += ht_print_chan(&sband->channels[i], 84 buf, buf_size, offset); 85 } 86 87 r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); 88 89 kfree(buf); 90 91 return r; 92 } 93 94 static const struct file_operations ht40allow_map_ops = { 95 .read = ht40allow_map_read, 96 .open = simple_open, 97 .llseek = default_llseek, 98 }; 99 100 #define DEBUGFS_ADD(name) \ 101 debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops) 102 103 void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) 104 { 105 struct dentry *phyd = rdev->wiphy.debugfsdir; 106 107 DEBUGFS_ADD(rts_threshold); 108 DEBUGFS_ADD(fragmentation_threshold); 109 DEBUGFS_ADD(short_retry_limit); 110 DEBUGFS_ADD(long_retry_limit); 111 DEBUGFS_ADD(ht40allow_map); 112 } 113 114 struct debugfs_read_work { 115 struct wiphy_work work; 116 ssize_t (*handler)(struct wiphy *wiphy, 117 struct file *file, 118 char *buf, 119 size_t count, 120 void *data); 121 struct wiphy *wiphy; 122 struct file *file; 123 char *buf; 124 size_t bufsize; 125 void *data; 126 ssize_t ret; 127 struct completion completion; 128 }; 129 130 static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy, 131 struct wiphy_work *work) 132 { 133 struct debugfs_read_work *w = container_of(work, typeof(*w), work); 134 135 w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data); 136 complete(&w->completion); 137 } 138 139 static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry, 140 void *data) 141 { 142 struct debugfs_read_work *w = data; 143 144 wiphy_work_cancel(w->wiphy, &w->work); 145 complete(&w->completion); 146 } 147 148 ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file, 149 char *buf, size_t bufsize, 150 char __user *userbuf, size_t count, 151 loff_t *ppos, 152 ssize_t (*handler)(struct wiphy *wiphy, 153 struct file *file, 154 char *buf, 155 size_t bufsize, 156 void *data), 157 void *data) 158 { 159 struct debugfs_read_work work = { 160 .handler = handler, 161 .wiphy = wiphy, 162 .file = file, 163 .buf = buf, 164 .bufsize = bufsize, 165 .data = data, 166 .ret = -ENODEV, 167 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion), 168 }; 169 struct debugfs_cancellation cancellation = { 170 .cancel = wiphy_locked_debugfs_read_cancel, 171 .cancel_data = &work, 172 }; 173 174 /* don't leak stack data or whatever */ 175 memset(buf, 0, bufsize); 176 177 wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work); 178 wiphy_work_queue(wiphy, &work.work); 179 180 debugfs_enter_cancellation(file, &cancellation); 181 wait_for_completion(&work.completion); 182 debugfs_leave_cancellation(file, &cancellation); 183 184 if (work.ret < 0) 185 return work.ret; 186 187 if (WARN_ON(work.ret > bufsize)) 188 return -EINVAL; 189 190 return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret); 191 } 192 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read); 193 194 struct debugfs_write_work { 195 struct wiphy_work work; 196 ssize_t (*handler)(struct wiphy *wiphy, 197 struct file *file, 198 char *buf, 199 size_t count, 200 void *data); 201 struct wiphy *wiphy; 202 struct file *file; 203 char *buf; 204 size_t count; 205 void *data; 206 ssize_t ret; 207 struct completion completion; 208 }; 209 210 static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy, 211 struct wiphy_work *work) 212 { 213 struct debugfs_write_work *w = container_of(work, typeof(*w), work); 214 215 w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data); 216 complete(&w->completion); 217 } 218 219 static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry, 220 void *data) 221 { 222 struct debugfs_write_work *w = data; 223 224 wiphy_work_cancel(w->wiphy, &w->work); 225 complete(&w->completion); 226 } 227 228 ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy, 229 struct file *file, char *buf, size_t bufsize, 230 const char __user *userbuf, size_t count, 231 ssize_t (*handler)(struct wiphy *wiphy, 232 struct file *file, 233 char *buf, 234 size_t count, 235 void *data), 236 void *data) 237 { 238 struct debugfs_write_work work = { 239 .handler = handler, 240 .wiphy = wiphy, 241 .file = file, 242 .buf = buf, 243 .count = count, 244 .data = data, 245 .ret = -ENODEV, 246 .completion = COMPLETION_INITIALIZER_ONSTACK(work.completion), 247 }; 248 struct debugfs_cancellation cancellation = { 249 .cancel = wiphy_locked_debugfs_write_cancel, 250 .cancel_data = &work, 251 }; 252 253 /* mostly used for strings so enforce NUL-termination for safety */ 254 if (count >= bufsize) 255 return -EINVAL; 256 257 memset(buf, 0, bufsize); 258 259 if (copy_from_user(buf, userbuf, count)) 260 return -EFAULT; 261 262 wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work); 263 wiphy_work_queue(wiphy, &work.work); 264 265 debugfs_enter_cancellation(file, &cancellation); 266 wait_for_completion(&work.completion); 267 debugfs_leave_cancellation(file, &cancellation); 268 269 return work.ret; 270 } 271 EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write); 272