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
25static 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;
37out:
38	return ret;
39}
40
41static 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
64static 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
98static 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
141static DEVICE_ATTR(interference, 0644,
142		   b43legacy_attr_interfmode_show,
143		   b43legacy_attr_interfmode_store);
144
145static 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
169static 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
194static DEVICE_ATTR(shortpreamble, 0644,
195		   b43legacy_attr_preamble_show,
196		   b43legacy_attr_preamble_store);
197
198int 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
213out:
214	return err;
215err_remove_interfmode:
216	device_remove_file(dev, &dev_attr_interference);
217	goto out;
218}
219
220void 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