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(tmp, 10, &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(buf, "true", 4) == 0)
49 			return 1;
50 		if (count >= 5 && memcmp(buf, "false", 5) == 0)
51 			return 0;
52 		if (count >= 3 && memcmp(buf, "yes", 3) == 0)
53 			return 1;
54 		if (count >= 2 && memcmp(buf, "no", 2) == 0)
55 			return 0;
56 		if (count >= 2 && memcmp(buf, "on", 2) == 0)
57 			return 1;
58 		if (count >= 3 && memcmp(buf, "off", 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, "0 (No Interference"
79 				 " Mitigation)\n");
80 		break;
81 	case B43legacy_INTERFMODE_NONWLAN:
82 		count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference"
83 				 " Mitigation)\n");
84 		break;
85 	case B43legacy_INTERFMODE_MANUALWLAN:
86 		count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference"
87 				 " Mitigation)\n");
88 		break;
89 	default:
90 		B43legacy_WARN_ON(1);
91 	}
92 
93 	mutex_unlock(&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(wldev, mode);
132 	if (err)
133 		b43legacyerr(wldev->wl, "Interference Mitigation not "
134 		       "supported by device\n");
135 	spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
136 	mutex_unlock(&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, "1 (Short Preamble"
159 				 " enabled)\n");
160 	else
161 		count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble"
162 				 " disabled)\n");
163 
164 	mutex_unlock(&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(&wldev->wl->irq_lock, flags);
189 	mutex_unlock(&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(dev, &dev_attr_interference);
207 	if (err)
208 		goto out;
209 	err = device_create_file(dev, &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, &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, &dev_attr_shortpreamble);
225 	device_remove_file(dev, &dev_attr_interference);
226 }
227