18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file provides /sys/class/ieee80211/<wiphy name>/
48c2ecf20Sopenharmony_ci * and some default attributes.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2005-2006	Jiri Benc <jbenc@suse.cz>
78c2ecf20Sopenharmony_ci * Copyright 2006	Johannes Berg <johannes@sipsolutions.net>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/nl80211.h>
148c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
158c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
168c2ecf20Sopenharmony_ci#include "sysfs.h"
178c2ecf20Sopenharmony_ci#include "core.h"
188c2ecf20Sopenharmony_ci#include "rdev-ops.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic inline struct cfg80211_registered_device *dev_to_rdev(
218c2ecf20Sopenharmony_ci	struct device *dev)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define SHOW_FMT(name, fmt, member)					\
278c2ecf20Sopenharmony_cistatic ssize_t name ## _show(struct device *dev,			\
288c2ecf20Sopenharmony_ci			      struct device_attribute *attr,		\
298c2ecf20Sopenharmony_ci			      char *buf)				\
308c2ecf20Sopenharmony_ci{									\
318c2ecf20Sopenharmony_ci	return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);	\
328c2ecf20Sopenharmony_ci}									\
338c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciSHOW_FMT(index, "%d", wiphy_idx);
368c2ecf20Sopenharmony_ciSHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
378c2ecf20Sopenharmony_ciSHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev,
408c2ecf20Sopenharmony_ci			 struct device_attribute *attr,
418c2ecf20Sopenharmony_ci			 char *buf)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", wiphy_name(wiphy));
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic ssize_t addresses_show(struct device *dev,
508c2ecf20Sopenharmony_ci			      struct device_attribute *attr,
518c2ecf20Sopenharmony_ci			      char *buf)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
548c2ecf20Sopenharmony_ci	char *start = buf;
558c2ecf20Sopenharmony_ci	int i;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (!wiphy->addresses)
588c2ecf20Sopenharmony_ci		return sprintf(buf, "%pM\n", wiphy->perm_addr);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	for (i = 0; i < wiphy->n_addresses; i++)
618c2ecf20Sopenharmony_ci		buf += sprintf(buf, "%pM\n", wiphy->addresses[i].addr);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return buf - start;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(addresses);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct attribute *ieee80211_attrs[] = {
688c2ecf20Sopenharmony_ci	&dev_attr_index.attr,
698c2ecf20Sopenharmony_ci	&dev_attr_macaddress.attr,
708c2ecf20Sopenharmony_ci	&dev_attr_address_mask.attr,
718c2ecf20Sopenharmony_ci	&dev_attr_addresses.attr,
728c2ecf20Sopenharmony_ci	&dev_attr_name.attr,
738c2ecf20Sopenharmony_ci	NULL,
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ieee80211);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void wiphy_dev_release(struct device *dev)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	cfg80211_dev_free(rdev);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	/* TODO, we probably need stuff here */
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
918c2ecf20Sopenharmony_cistatic void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct wireless_dev *wdev;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
968c2ecf20Sopenharmony_ci		cfg80211_leave(rdev, wdev);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int wiphy_suspend(struct device *dev)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
1028c2ecf20Sopenharmony_ci	int ret = 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	rdev->suspend_at = ktime_get_boottime_seconds();
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	rtnl_lock();
1078c2ecf20Sopenharmony_ci	if (rdev->wiphy.registered) {
1088c2ecf20Sopenharmony_ci		if (!rdev->wiphy.wowlan_config) {
1098c2ecf20Sopenharmony_ci			cfg80211_leave_all(rdev);
1108c2ecf20Sopenharmony_ci			cfg80211_process_rdev_events(rdev);
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci		if (rdev->ops->suspend)
1138c2ecf20Sopenharmony_ci			ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
1148c2ecf20Sopenharmony_ci		if (ret == 1) {
1158c2ecf20Sopenharmony_ci			/* Driver refuse to configure wowlan */
1168c2ecf20Sopenharmony_ci			cfg80211_leave_all(rdev);
1178c2ecf20Sopenharmony_ci			cfg80211_process_rdev_events(rdev);
1188c2ecf20Sopenharmony_ci			ret = rdev_suspend(rdev, NULL);
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	rtnl_unlock();
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int wiphy_resume(struct device *dev)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
1298c2ecf20Sopenharmony_ci	int ret = 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Age scan results with time spent in suspend */
1328c2ecf20Sopenharmony_ci	cfg80211_bss_age(rdev, ktime_get_boottime_seconds() - rdev->suspend_at);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	rtnl_lock();
1358c2ecf20Sopenharmony_ci	if (rdev->wiphy.registered && rdev->ops->resume)
1368c2ecf20Sopenharmony_ci		ret = rdev_resume(rdev);
1378c2ecf20Sopenharmony_ci	rtnl_unlock();
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return ret;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(wiphy_pm_ops, wiphy_suspend, wiphy_resume);
1438c2ecf20Sopenharmony_ci#define WIPHY_PM_OPS (&wiphy_pm_ops)
1448c2ecf20Sopenharmony_ci#else
1458c2ecf20Sopenharmony_ci#define WIPHY_PM_OPS NULL
1468c2ecf20Sopenharmony_ci#endif
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic const void *wiphy_namespace(struct device *d)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct wiphy *wiphy = container_of(d, struct wiphy, dev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return wiphy_net(wiphy);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistruct class ieee80211_class = {
1568c2ecf20Sopenharmony_ci	.name = "ieee80211",
1578c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1588c2ecf20Sopenharmony_ci	.dev_release = wiphy_dev_release,
1598c2ecf20Sopenharmony_ci	.dev_groups = ieee80211_groups,
1608c2ecf20Sopenharmony_ci	.dev_uevent = wiphy_uevent,
1618c2ecf20Sopenharmony_ci	.pm = WIPHY_PM_OPS,
1628c2ecf20Sopenharmony_ci	.ns_type = &net_ns_type_operations,
1638c2ecf20Sopenharmony_ci	.namespace = wiphy_namespace,
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ciint wiphy_sysfs_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return class_register(&ieee80211_class);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_civoid wiphy_sysfs_exit(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	class_unregister(&ieee80211_class);
1748c2ecf20Sopenharmony_ci}
175