18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux WiMAX
48c2ecf20Sopenharmony_ci * Implement and export a method for resetting a WiMAX device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
78c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This implements a simple synchronous call to reset a WiMAX device.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Resets aim at being warm, keeping the device handles active;
128c2ecf20Sopenharmony_ci * however, when that fails, it falls back to a cold reset (that will
138c2ecf20Sopenharmony_ci * disconnect and reconnect the device).
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <net/wimax.h>
178c2ecf20Sopenharmony_ci#include <net/genetlink.h>
188c2ecf20Sopenharmony_ci#include <linux/wimax.h>
198c2ecf20Sopenharmony_ci#include <linux/security.h>
208c2ecf20Sopenharmony_ci#include <linux/export.h>
218c2ecf20Sopenharmony_ci#include "wimax-internal.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define D_SUBMODULE op_reset
248c2ecf20Sopenharmony_ci#include "debug-levels.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/**
288c2ecf20Sopenharmony_ci * wimax_reset - Reset a WiMAX device
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Returns:
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * %0 if ok and a warm reset was done (the device still exists in
358c2ecf20Sopenharmony_ci * the system).
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * -%ENODEV if a cold/bus reset had to be done (device has
388c2ecf20Sopenharmony_ci * disconnected and reconnected, so current handle is not valid
398c2ecf20Sopenharmony_ci * any more).
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * -%EINVAL if the device is not even registered.
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Any other negative error code shall be considered as
448c2ecf20Sopenharmony_ci * non-recoverable.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Description:
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * Called when wanting to reset the device for any reason. Device is
498c2ecf20Sopenharmony_ci * taken back to power on status.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * This call blocks; on successful return, the device has completed the
528c2ecf20Sopenharmony_ci * reset process and is ready to operate.
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ciint wimax_reset(struct wimax_dev *wimax_dev)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	int result = -EINVAL;
578c2ecf20Sopenharmony_ci	struct device *dev = wimax_dev_to_dev(wimax_dev);
588c2ecf20Sopenharmony_ci	enum wimax_st state;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	might_sleep();
618c2ecf20Sopenharmony_ci	d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
628c2ecf20Sopenharmony_ci	mutex_lock(&wimax_dev->mutex);
638c2ecf20Sopenharmony_ci	dev_hold(wimax_dev->net_dev);
648c2ecf20Sopenharmony_ci	state = wimax_dev->state;
658c2ecf20Sopenharmony_ci	mutex_unlock(&wimax_dev->mutex);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (state >= WIMAX_ST_DOWN) {
688c2ecf20Sopenharmony_ci		mutex_lock(&wimax_dev->mutex_reset);
698c2ecf20Sopenharmony_ci		result = wimax_dev->op_reset(wimax_dev);
708c2ecf20Sopenharmony_ci		mutex_unlock(&wimax_dev->mutex_reset);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci	dev_put(wimax_dev->net_dev);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
758c2ecf20Sopenharmony_ci	return result;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wimax_reset);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Exporting to user space over generic netlink
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * Parse the reset command from user space, return error code.
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * No attributes.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_ciint wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int result, ifindex;
908c2ecf20Sopenharmony_ci	struct wimax_dev *wimax_dev;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
938c2ecf20Sopenharmony_ci	result = -ENODEV;
948c2ecf20Sopenharmony_ci	if (info->attrs[WIMAX_GNL_RESET_IFIDX] == NULL) {
958c2ecf20Sopenharmony_ci		pr_err("WIMAX_GNL_OP_RFKILL: can't find IFIDX attribute\n");
968c2ecf20Sopenharmony_ci		goto error_no_wimax_dev;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RESET_IFIDX]);
998c2ecf20Sopenharmony_ci	wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
1008c2ecf20Sopenharmony_ci	if (wimax_dev == NULL)
1018c2ecf20Sopenharmony_ci		goto error_no_wimax_dev;
1028c2ecf20Sopenharmony_ci	/* Execute the operation and send the result back to user space */
1038c2ecf20Sopenharmony_ci	result = wimax_reset(wimax_dev);
1048c2ecf20Sopenharmony_ci	dev_put(wimax_dev->net_dev);
1058c2ecf20Sopenharmony_cierror_no_wimax_dev:
1068c2ecf20Sopenharmony_ci	d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
1078c2ecf20Sopenharmony_ci	return result;
1088c2ecf20Sopenharmony_ci}
109