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