18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * lib80211 -- common bits for IEEE802.11 drivers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Portions copied from old ieee80211 component, w/ original copyright
88c2ecf20Sopenharmony_ci * notices below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Host AP crypto routines
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
138c2ecf20Sopenharmony_ci * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/ctype.h>
218c2ecf20Sopenharmony_ci#include <linux/ieee80211.h>
228c2ecf20Sopenharmony_ci#include <linux/errno.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/string.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <net/lib80211.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define DRV_DESCRIPTION	"common routines for IEEE802.11 drivers"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
328c2ecf20Sopenharmony_ciMODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct lib80211_crypto_alg {
368c2ecf20Sopenharmony_ci	struct list_head list;
378c2ecf20Sopenharmony_ci	struct lib80211_crypto_ops *ops;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic LIST_HEAD(lib80211_crypto_algs);
418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(lib80211_crypto_lock);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
448c2ecf20Sopenharmony_ci					  int force);
458c2ecf20Sopenharmony_cistatic void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
468c2ecf20Sopenharmony_cistatic void lib80211_crypt_deinit_handler(struct timer_list *t);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
498c2ecf20Sopenharmony_ci				spinlock_t *lock)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	memset(info, 0, sizeof(*info));
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	info->name = name;
548c2ecf20Sopenharmony_ci	info->lock = lock;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->crypt_deinit_list);
578c2ecf20Sopenharmony_ci	timer_setup(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
588c2ecf20Sopenharmony_ci		    0);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_crypt_info_init);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_civoid lib80211_crypt_info_free(struct lib80211_crypt_info *info)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int i;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci        lib80211_crypt_quiescing(info);
698c2ecf20Sopenharmony_ci        del_timer_sync(&info->crypt_deinit_timer);
708c2ecf20Sopenharmony_ci        lib80211_crypt_deinit_entries(info, 1);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci        for (i = 0; i < NUM_WEP_KEYS; i++) {
738c2ecf20Sopenharmony_ci                struct lib80211_crypt_data *crypt = info->crypt[i];
748c2ecf20Sopenharmony_ci                if (crypt) {
758c2ecf20Sopenharmony_ci                        if (crypt->ops) {
768c2ecf20Sopenharmony_ci                                crypt->ops->deinit(crypt->priv);
778c2ecf20Sopenharmony_ci                                module_put(crypt->ops->owner);
788c2ecf20Sopenharmony_ci                        }
798c2ecf20Sopenharmony_ci                        kfree(crypt);
808c2ecf20Sopenharmony_ci                        info->crypt[i] = NULL;
818c2ecf20Sopenharmony_ci                }
828c2ecf20Sopenharmony_ci        }
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_crypt_info_free);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
878c2ecf20Sopenharmony_ci					  int force)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct lib80211_crypt_data *entry, *next;
908c2ecf20Sopenharmony_ci	unsigned long flags;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	spin_lock_irqsave(info->lock, flags);
938c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
948c2ecf20Sopenharmony_ci		if (atomic_read(&entry->refcnt) != 0 && !force)
958c2ecf20Sopenharmony_ci			continue;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		list_del(&entry->list);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		if (entry->ops) {
1008c2ecf20Sopenharmony_ci			entry->ops->deinit(entry->priv);
1018c2ecf20Sopenharmony_ci			module_put(entry->ops->owner);
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci		kfree(entry);
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(info->lock, flags);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* After this, crypt_deinit_list won't accept new members */
1098c2ecf20Sopenharmony_cistatic void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	unsigned long flags;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	spin_lock_irqsave(info->lock, flags);
1148c2ecf20Sopenharmony_ci	info->crypt_quiesced = 1;
1158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(info->lock, flags);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void lib80211_crypt_deinit_handler(struct timer_list *t)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct lib80211_crypt_info *info = from_timer(info, t,
1218c2ecf20Sopenharmony_ci						      crypt_deinit_timer);
1228c2ecf20Sopenharmony_ci	unsigned long flags;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	lib80211_crypt_deinit_entries(info, 0);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	spin_lock_irqsave(info->lock, flags);
1278c2ecf20Sopenharmony_ci	if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
1288c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
1298c2ecf20Sopenharmony_ci		       "deletion list\n", info->name);
1308c2ecf20Sopenharmony_ci		info->crypt_deinit_timer.expires = jiffies + HZ;
1318c2ecf20Sopenharmony_ci		add_timer(&info->crypt_deinit_timer);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(info->lock, flags);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_civoid lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
1378c2ecf20Sopenharmony_ci				    struct lib80211_crypt_data **crypt)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct lib80211_crypt_data *tmp;
1408c2ecf20Sopenharmony_ci	unsigned long flags;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (*crypt == NULL)
1438c2ecf20Sopenharmony_ci		return;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	tmp = *crypt;
1468c2ecf20Sopenharmony_ci	*crypt = NULL;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* must not run ops->deinit() while there may be pending encrypt or
1498c2ecf20Sopenharmony_ci	 * decrypt operations. Use a list of delayed deinits to avoid needing
1508c2ecf20Sopenharmony_ci	 * locking. */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	spin_lock_irqsave(info->lock, flags);
1538c2ecf20Sopenharmony_ci	if (!info->crypt_quiesced) {
1548c2ecf20Sopenharmony_ci		list_add(&tmp->list, &info->crypt_deinit_list);
1558c2ecf20Sopenharmony_ci		if (!timer_pending(&info->crypt_deinit_timer)) {
1568c2ecf20Sopenharmony_ci			info->crypt_deinit_timer.expires = jiffies + HZ;
1578c2ecf20Sopenharmony_ci			add_timer(&info->crypt_deinit_timer);
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(info->lock, flags);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ciint lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	unsigned long flags;
1678c2ecf20Sopenharmony_ci	struct lib80211_crypto_alg *alg;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	alg = kzalloc(sizeof(*alg), GFP_KERNEL);
1708c2ecf20Sopenharmony_ci	if (alg == NULL)
1718c2ecf20Sopenharmony_ci		return -ENOMEM;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	alg->ops = ops;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lib80211_crypto_lock, flags);
1768c2ecf20Sopenharmony_ci	list_add(&alg->list, &lib80211_crypto_algs);
1778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
1808c2ecf20Sopenharmony_ci	       ops->name);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_register_crypto_ops);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciint lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct lib80211_crypto_alg *alg;
1898c2ecf20Sopenharmony_ci	unsigned long flags;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lib80211_crypto_lock, flags);
1928c2ecf20Sopenharmony_ci	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
1938c2ecf20Sopenharmony_ci		if (alg->ops == ops)
1948c2ecf20Sopenharmony_ci			goto found;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
1978c2ecf20Sopenharmony_ci	return -EINVAL;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci      found:
2008c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n",
2018c2ecf20Sopenharmony_ci	       ops->name);
2028c2ecf20Sopenharmony_ci	list_del(&alg->list);
2038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
2048c2ecf20Sopenharmony_ci	kfree(alg);
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_unregister_crypto_ops);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct lib80211_crypto_alg *alg;
2128c2ecf20Sopenharmony_ci	unsigned long flags;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lib80211_crypto_lock, flags);
2158c2ecf20Sopenharmony_ci	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
2168c2ecf20Sopenharmony_ci		if (strcmp(alg->ops->name, name) == 0)
2178c2ecf20Sopenharmony_ci			goto found;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
2208c2ecf20Sopenharmony_ci	return NULL;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci      found:
2238c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
2248c2ecf20Sopenharmony_ci	return alg->ops;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lib80211_get_crypto_ops);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void *lib80211_crypt_null_init(int keyidx)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	return (void *)1;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void lib80211_crypt_null_deinit(void *priv)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic struct lib80211_crypto_ops lib80211_crypt_null = {
2388c2ecf20Sopenharmony_ci	.name = "NULL",
2398c2ecf20Sopenharmony_ci	.init = lib80211_crypt_null_init,
2408c2ecf20Sopenharmony_ci	.deinit = lib80211_crypt_null_deinit,
2418c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int __init lib80211_init(void)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	pr_info(DRV_DESCRIPTION "\n");
2478c2ecf20Sopenharmony_ci	return lib80211_register_crypto_ops(&lib80211_crypt_null);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void __exit lib80211_exit(void)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	lib80211_unregister_crypto_ops(&lib80211_crypt_null);
2538c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&lib80211_crypto_algs));
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cimodule_init(lib80211_init);
2578c2ecf20Sopenharmony_cimodule_exit(lib80211_exit);
258