18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci  Broadcom B43legacy wireless driver
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci  SYSFS support routines
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci  Copyright (c) 2006 Michael Buesch <m@bues.ch>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci*/
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "sysfs.h"
148c2ecf20Sopenharmony_ci#include "b43legacy.h"
158c2ecf20Sopenharmony_ci#include "main.h"
168c2ecf20Sopenharmony_ci#include "phy.h"
178c2ecf20Sopenharmony_ci#include "radio.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/capability.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define GENERIC_FILESIZE	64
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int get_integer(const char *buf, size_t count)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	char tmp[10 + 1] = { 0 };
288c2ecf20Sopenharmony_ci	int ret = -EINVAL, res;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (count == 0)
318c2ecf20Sopenharmony_ci		goto out;
328c2ecf20Sopenharmony_ci	count = min_t(size_t, count, 10);
338c2ecf20Sopenharmony_ci	memcpy(tmp, buf, count);
348c2ecf20Sopenharmony_ci	ret = kstrtoint(tmp, 10, &res);
358c2ecf20Sopenharmony_ci	if (!ret)
368c2ecf20Sopenharmony_ci		return res;
378c2ecf20Sopenharmony_ciout:
388c2ecf20Sopenharmony_ci	return ret;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int get_boolean(const char *buf, size_t count)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	if (count != 0) {
448c2ecf20Sopenharmony_ci		if (buf[0] == '1')
458c2ecf20Sopenharmony_ci			return 1;
468c2ecf20Sopenharmony_ci		if (buf[0] == '0')
478c2ecf20Sopenharmony_ci			return 0;
488c2ecf20Sopenharmony_ci		if (count >= 4 && memcmp(buf, "true", 4) == 0)
498c2ecf20Sopenharmony_ci			return 1;
508c2ecf20Sopenharmony_ci		if (count >= 5 && memcmp(buf, "false", 5) == 0)
518c2ecf20Sopenharmony_ci			return 0;
528c2ecf20Sopenharmony_ci		if (count >= 3 && memcmp(buf, "yes", 3) == 0)
538c2ecf20Sopenharmony_ci			return 1;
548c2ecf20Sopenharmony_ci		if (count >= 2 && memcmp(buf, "no", 2) == 0)
558c2ecf20Sopenharmony_ci			return 0;
568c2ecf20Sopenharmony_ci		if (count >= 2 && memcmp(buf, "on", 2) == 0)
578c2ecf20Sopenharmony_ci			return 1;
588c2ecf20Sopenharmony_ci		if (count >= 3 && memcmp(buf, "off", 3) == 0)
598c2ecf20Sopenharmony_ci			return 0;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	return -EINVAL;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic ssize_t b43legacy_attr_interfmode_show(struct device *dev,
658c2ecf20Sopenharmony_ci					      struct device_attribute *attr,
668c2ecf20Sopenharmony_ci					      char *buf)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
698c2ecf20Sopenharmony_ci	ssize_t count = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
728c2ecf20Sopenharmony_ci		return -EPERM;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	mutex_lock(&wldev->wl->mutex);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	switch (wldev->phy.interfmode) {
778c2ecf20Sopenharmony_ci	case B43legacy_INTERFMODE_NONE:
788c2ecf20Sopenharmony_ci		count = snprintf(buf, PAGE_SIZE, "0 (No Interference"
798c2ecf20Sopenharmony_ci				 " Mitigation)\n");
808c2ecf20Sopenharmony_ci		break;
818c2ecf20Sopenharmony_ci	case B43legacy_INTERFMODE_NONWLAN:
828c2ecf20Sopenharmony_ci		count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference"
838c2ecf20Sopenharmony_ci				 " Mitigation)\n");
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case B43legacy_INTERFMODE_MANUALWLAN:
868c2ecf20Sopenharmony_ci		count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference"
878c2ecf20Sopenharmony_ci				 " Mitigation)\n");
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci	default:
908c2ecf20Sopenharmony_ci		B43legacy_WARN_ON(1);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mutex_unlock(&wldev->wl->mutex);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return count;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic ssize_t b43legacy_attr_interfmode_store(struct device *dev,
998c2ecf20Sopenharmony_ci					       struct device_attribute *attr,
1008c2ecf20Sopenharmony_ci					       const char *buf, size_t count)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
1038c2ecf20Sopenharmony_ci	unsigned long flags;
1048c2ecf20Sopenharmony_ci	int err;
1058c2ecf20Sopenharmony_ci	int mode;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
1088c2ecf20Sopenharmony_ci		return -EPERM;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	mode = get_integer(buf, count);
1118c2ecf20Sopenharmony_ci	switch (mode) {
1128c2ecf20Sopenharmony_ci	case 0:
1138c2ecf20Sopenharmony_ci		mode = B43legacy_INTERFMODE_NONE;
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci	case 1:
1168c2ecf20Sopenharmony_ci		mode = B43legacy_INTERFMODE_NONWLAN;
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	case 2:
1198c2ecf20Sopenharmony_ci		mode = B43legacy_INTERFMODE_MANUALWLAN;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci	case 3:
1228c2ecf20Sopenharmony_ci		mode = B43legacy_INTERFMODE_AUTOWLAN;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	default:
1258c2ecf20Sopenharmony_ci		return -EINVAL;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	mutex_lock(&wldev->wl->mutex);
1298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wldev->wl->irq_lock, flags);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	err = b43legacy_radio_set_interference_mitigation(wldev, mode);
1328c2ecf20Sopenharmony_ci	if (err)
1338c2ecf20Sopenharmony_ci		b43legacyerr(wldev->wl, "Interference Mitigation not "
1348c2ecf20Sopenharmony_ci		       "supported by device\n");
1358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
1368c2ecf20Sopenharmony_ci	mutex_unlock(&wldev->wl->mutex);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return err ? err : count;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic DEVICE_ATTR(interference, 0644,
1428c2ecf20Sopenharmony_ci		   b43legacy_attr_interfmode_show,
1438c2ecf20Sopenharmony_ci		   b43legacy_attr_interfmode_store);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic ssize_t b43legacy_attr_preamble_show(struct device *dev,
1468c2ecf20Sopenharmony_ci					    struct device_attribute *attr,
1478c2ecf20Sopenharmony_ci					    char *buf)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
1508c2ecf20Sopenharmony_ci	ssize_t count;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
1538c2ecf20Sopenharmony_ci		return -EPERM;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	mutex_lock(&wldev->wl->mutex);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (wldev->short_preamble)
1588c2ecf20Sopenharmony_ci		count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble"
1598c2ecf20Sopenharmony_ci				 " enabled)\n");
1608c2ecf20Sopenharmony_ci	else
1618c2ecf20Sopenharmony_ci		count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble"
1628c2ecf20Sopenharmony_ci				 " disabled)\n");
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	mutex_unlock(&wldev->wl->mutex);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return count;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic ssize_t b43legacy_attr_preamble_store(struct device *dev,
1708c2ecf20Sopenharmony_ci					     struct device_attribute *attr,
1718c2ecf20Sopenharmony_ci					     const char *buf, size_t count)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev);
1748c2ecf20Sopenharmony_ci	unsigned long flags;
1758c2ecf20Sopenharmony_ci	int value;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
1788c2ecf20Sopenharmony_ci		return -EPERM;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	value = get_boolean(buf, count);
1818c2ecf20Sopenharmony_ci	if (value < 0)
1828c2ecf20Sopenharmony_ci		return value;
1838c2ecf20Sopenharmony_ci	mutex_lock(&wldev->wl->mutex);
1848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wldev->wl->irq_lock, flags);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	wldev->short_preamble = !!value;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
1898c2ecf20Sopenharmony_ci	mutex_unlock(&wldev->wl->mutex);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return count;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic DEVICE_ATTR(shortpreamble, 0644,
1958c2ecf20Sopenharmony_ci		   b43legacy_attr_preamble_show,
1968c2ecf20Sopenharmony_ci		   b43legacy_attr_preamble_store);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciint b43legacy_sysfs_register(struct b43legacy_wldev *wldev)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct device *dev = wldev->dev->dev;
2018c2ecf20Sopenharmony_ci	int err;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	B43legacy_WARN_ON(b43legacy_status(wldev) !=
2048c2ecf20Sopenharmony_ci			  B43legacy_STAT_INITIALIZED);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	err = device_create_file(dev, &dev_attr_interference);
2078c2ecf20Sopenharmony_ci	if (err)
2088c2ecf20Sopenharmony_ci		goto out;
2098c2ecf20Sopenharmony_ci	err = device_create_file(dev, &dev_attr_shortpreamble);
2108c2ecf20Sopenharmony_ci	if (err)
2118c2ecf20Sopenharmony_ci		goto err_remove_interfmode;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciout:
2148c2ecf20Sopenharmony_ci	return err;
2158c2ecf20Sopenharmony_cierr_remove_interfmode:
2168c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_interference);
2178c2ecf20Sopenharmony_ci	goto out;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_civoid b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct device *dev = wldev->dev->dev;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_shortpreamble);
2258c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_interference);
2268c2ecf20Sopenharmony_ci}
227