1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | |
4 | Broadcom B43legacy wireless driver |
5 | |
6 | SYSFS support routines |
7 | |
8 | Copyright (c) 2006 Michael Buesch <m@bues.ch> |
9 | |
10 | |
11 | */ |
12 | |
13 | #include "sysfs.h" |
14 | #include "b43legacy.h" |
15 | #include "main.h" |
16 | #include "phy.h" |
17 | #include "radio.h" |
18 | |
19 | #include <linux/capability.h> |
20 | |
21 | |
22 | #define GENERIC_FILESIZE 64 |
23 | |
24 | |
25 | static int get_integer(const char *buf, size_t count) |
26 | { |
27 | char tmp[10 + 1] = { 0 }; |
28 | int ret = -EINVAL, res; |
29 | |
30 | if (count == 0) |
31 | goto out; |
32 | count = min_t(size_t, count, 10); |
33 | memcpy(tmp, buf, count); |
34 | ret = kstrtoint(s: tmp, base: 10, res: &res); |
35 | if (!ret) |
36 | return res; |
37 | out: |
38 | return ret; |
39 | } |
40 | |
41 | static int get_boolean(const char *buf, size_t count) |
42 | { |
43 | if (count != 0) { |
44 | if (buf[0] == '1') |
45 | return 1; |
46 | if (buf[0] == '0') |
47 | return 0; |
48 | if (count >= 4 && memcmp(p: buf, q: "true" , size: 4) == 0) |
49 | return 1; |
50 | if (count >= 5 && memcmp(p: buf, q: "false" , size: 5) == 0) |
51 | return 0; |
52 | if (count >= 3 && memcmp(p: buf, q: "yes" , size: 3) == 0) |
53 | return 1; |
54 | if (count >= 2 && memcmp(p: buf, q: "no" , size: 2) == 0) |
55 | return 0; |
56 | if (count >= 2 && memcmp(p: buf, q: "on" , size: 2) == 0) |
57 | return 1; |
58 | if (count >= 3 && memcmp(p: buf, q: "off" , size: 3) == 0) |
59 | return 0; |
60 | } |
61 | return -EINVAL; |
62 | } |
63 | |
64 | static ssize_t b43legacy_attr_interfmode_show(struct device *dev, |
65 | struct device_attribute *attr, |
66 | char *buf) |
67 | { |
68 | struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); |
69 | ssize_t count = 0; |
70 | |
71 | if (!capable(CAP_NET_ADMIN)) |
72 | return -EPERM; |
73 | |
74 | mutex_lock(&wldev->wl->mutex); |
75 | |
76 | switch (wldev->phy.interfmode) { |
77 | case B43legacy_INTERFMODE_NONE: |
78 | count = snprintf(buf, PAGE_SIZE, fmt: "0 (No Interference" |
79 | " Mitigation)\n" ); |
80 | break; |
81 | case B43legacy_INTERFMODE_NONWLAN: |
82 | count = snprintf(buf, PAGE_SIZE, fmt: "1 (Non-WLAN Interference" |
83 | " Mitigation)\n" ); |
84 | break; |
85 | case B43legacy_INTERFMODE_MANUALWLAN: |
86 | count = snprintf(buf, PAGE_SIZE, fmt: "2 (WLAN Interference" |
87 | " Mitigation)\n" ); |
88 | break; |
89 | default: |
90 | B43legacy_WARN_ON(1); |
91 | } |
92 | |
93 | mutex_unlock(lock: &wldev->wl->mutex); |
94 | |
95 | return count; |
96 | } |
97 | |
98 | static ssize_t b43legacy_attr_interfmode_store(struct device *dev, |
99 | struct device_attribute *attr, |
100 | const char *buf, size_t count) |
101 | { |
102 | struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); |
103 | unsigned long flags; |
104 | int err; |
105 | int mode; |
106 | |
107 | if (!capable(CAP_NET_ADMIN)) |
108 | return -EPERM; |
109 | |
110 | mode = get_integer(buf, count); |
111 | switch (mode) { |
112 | case 0: |
113 | mode = B43legacy_INTERFMODE_NONE; |
114 | break; |
115 | case 1: |
116 | mode = B43legacy_INTERFMODE_NONWLAN; |
117 | break; |
118 | case 2: |
119 | mode = B43legacy_INTERFMODE_MANUALWLAN; |
120 | break; |
121 | case 3: |
122 | mode = B43legacy_INTERFMODE_AUTOWLAN; |
123 | break; |
124 | default: |
125 | return -EINVAL; |
126 | } |
127 | |
128 | mutex_lock(&wldev->wl->mutex); |
129 | spin_lock_irqsave(&wldev->wl->irq_lock, flags); |
130 | |
131 | err = b43legacy_radio_set_interference_mitigation(dev: wldev, mode); |
132 | if (err) |
133 | b43legacyerr(wl: wldev->wl, fmt: "Interference Mitigation not " |
134 | "supported by device\n" ); |
135 | spin_unlock_irqrestore(lock: &wldev->wl->irq_lock, flags); |
136 | mutex_unlock(lock: &wldev->wl->mutex); |
137 | |
138 | return err ? err : count; |
139 | } |
140 | |
141 | static DEVICE_ATTR(interference, 0644, |
142 | b43legacy_attr_interfmode_show, |
143 | b43legacy_attr_interfmode_store); |
144 | |
145 | static ssize_t b43legacy_attr_preamble_show(struct device *dev, |
146 | struct device_attribute *attr, |
147 | char *buf) |
148 | { |
149 | struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); |
150 | ssize_t count; |
151 | |
152 | if (!capable(CAP_NET_ADMIN)) |
153 | return -EPERM; |
154 | |
155 | mutex_lock(&wldev->wl->mutex); |
156 | |
157 | if (wldev->short_preamble) |
158 | count = snprintf(buf, PAGE_SIZE, fmt: "1 (Short Preamble" |
159 | " enabled)\n" ); |
160 | else |
161 | count = snprintf(buf, PAGE_SIZE, fmt: "0 (Short Preamble" |
162 | " disabled)\n" ); |
163 | |
164 | mutex_unlock(lock: &wldev->wl->mutex); |
165 | |
166 | return count; |
167 | } |
168 | |
169 | static ssize_t b43legacy_attr_preamble_store(struct device *dev, |
170 | struct device_attribute *attr, |
171 | const char *buf, size_t count) |
172 | { |
173 | struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); |
174 | unsigned long flags; |
175 | int value; |
176 | |
177 | if (!capable(CAP_NET_ADMIN)) |
178 | return -EPERM; |
179 | |
180 | value = get_boolean(buf, count); |
181 | if (value < 0) |
182 | return value; |
183 | mutex_lock(&wldev->wl->mutex); |
184 | spin_lock_irqsave(&wldev->wl->irq_lock, flags); |
185 | |
186 | wldev->short_preamble = !!value; |
187 | |
188 | spin_unlock_irqrestore(lock: &wldev->wl->irq_lock, flags); |
189 | mutex_unlock(lock: &wldev->wl->mutex); |
190 | |
191 | return count; |
192 | } |
193 | |
194 | static DEVICE_ATTR(shortpreamble, 0644, |
195 | b43legacy_attr_preamble_show, |
196 | b43legacy_attr_preamble_store); |
197 | |
198 | int b43legacy_sysfs_register(struct b43legacy_wldev *wldev) |
199 | { |
200 | struct device *dev = wldev->dev->dev; |
201 | int err; |
202 | |
203 | B43legacy_WARN_ON(b43legacy_status(wldev) != |
204 | B43legacy_STAT_INITIALIZED); |
205 | |
206 | err = device_create_file(device: dev, entry: &dev_attr_interference); |
207 | if (err) |
208 | goto out; |
209 | err = device_create_file(device: dev, entry: &dev_attr_shortpreamble); |
210 | if (err) |
211 | goto err_remove_interfmode; |
212 | |
213 | out: |
214 | return err; |
215 | err_remove_interfmode: |
216 | device_remove_file(dev, attr: &dev_attr_interference); |
217 | goto out; |
218 | } |
219 | |
220 | void b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev) |
221 | { |
222 | struct device *dev = wldev->dev->dev; |
223 | |
224 | device_remove_file(dev, attr: &dev_attr_shortpreamble); |
225 | device_remove_file(dev, attr: &dev_attr_interference); |
226 | } |
227 | |