1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27e272fcfSJohn W. Linville /* 37e272fcfSJohn W. Linville * lib80211 -- common bits for IEEE802.11 drivers 47e272fcfSJohn W. Linville * 57e272fcfSJohn W. Linville * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com> 67e272fcfSJohn W. Linville * 7274bfb8dSJohn W. Linville * Portions copied from old ieee80211 component, w/ original copyright 8274bfb8dSJohn W. Linville * notices below: 9274bfb8dSJohn W. Linville * 10274bfb8dSJohn W. Linville * Host AP crypto routines 11274bfb8dSJohn W. Linville * 12274bfb8dSJohn W. Linville * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> 13274bfb8dSJohn W. Linville * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> 14274bfb8dSJohn W. Linville * 157e272fcfSJohn W. Linville */ 167e272fcfSJohn W. Linville 17e9c0268fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18e9c0268fSJoe Perches 197e272fcfSJohn W. Linville #include <linux/module.h> 202819f8adSJohn W. Linville #include <linux/ctype.h> 217e272fcfSJohn W. Linville #include <linux/ieee80211.h> 22274bfb8dSJohn W. Linville #include <linux/errno.h> 23274bfb8dSJohn W. Linville #include <linux/init.h> 24274bfb8dSJohn W. Linville #include <linux/slab.h> 25274bfb8dSJohn W. Linville #include <linux/string.h> 267e272fcfSJohn W. Linville 277e272fcfSJohn W. Linville #include <net/lib80211.h> 287e272fcfSJohn W. Linville 297e272fcfSJohn W. Linville #define DRV_NAME "lib80211" 307e272fcfSJohn W. Linville 317e272fcfSJohn W. Linville #define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" 327e272fcfSJohn W. Linville 337e272fcfSJohn W. Linville MODULE_DESCRIPTION(DRV_DESCRIPTION); 347e272fcfSJohn W. Linville MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>"); 357e272fcfSJohn W. Linville MODULE_LICENSE("GPL"); 367e272fcfSJohn W. Linville 37274bfb8dSJohn W. Linville struct lib80211_crypto_alg { 38274bfb8dSJohn W. Linville struct list_head list; 39274bfb8dSJohn W. Linville struct lib80211_crypto_ops *ops; 40274bfb8dSJohn W. Linville }; 41274bfb8dSJohn W. Linville 42274bfb8dSJohn W. Linville static LIST_HEAD(lib80211_crypto_algs); 43274bfb8dSJohn W. Linville static DEFINE_SPINLOCK(lib80211_crypto_lock); 44274bfb8dSJohn W. Linville 459d630c77SPavel Roskin static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, 469d630c77SPavel Roskin int force); 479d630c77SPavel Roskin static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); 48e99e88a9SKees Cook static void lib80211_crypt_deinit_handler(struct timer_list *t); 499d630c77SPavel Roskin 502ba4b32eSJohn W. Linville int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, 512ba4b32eSJohn W. Linville spinlock_t *lock) 522ba4b32eSJohn W. Linville { 532ba4b32eSJohn W. Linville memset(info, 0, sizeof(*info)); 542ba4b32eSJohn W. Linville 552ba4b32eSJohn W. Linville info->name = name; 562ba4b32eSJohn W. Linville info->lock = lock; 572ba4b32eSJohn W. Linville 582ba4b32eSJohn W. Linville INIT_LIST_HEAD(&info->crypt_deinit_list); 59e99e88a9SKees Cook timer_setup(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, 60e99e88a9SKees Cook 0); 612ba4b32eSJohn W. Linville 622ba4b32eSJohn W. Linville return 0; 632ba4b32eSJohn W. Linville } 642ba4b32eSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_info_init); 652ba4b32eSJohn W. Linville 662ba4b32eSJohn W. Linville void lib80211_crypt_info_free(struct lib80211_crypt_info *info) 672ba4b32eSJohn W. Linville { 682ba4b32eSJohn W. Linville int i; 692ba4b32eSJohn W. Linville 702ba4b32eSJohn W. Linville lib80211_crypt_quiescing(info); 712ba4b32eSJohn W. Linville del_timer_sync(&info->crypt_deinit_timer); 722ba4b32eSJohn W. Linville lib80211_crypt_deinit_entries(info, 1); 732ba4b32eSJohn W. Linville 742ba4b32eSJohn W. Linville for (i = 0; i < NUM_WEP_KEYS; i++) { 752ba4b32eSJohn W. Linville struct lib80211_crypt_data *crypt = info->crypt[i]; 762ba4b32eSJohn W. Linville if (crypt) { 772ba4b32eSJohn W. Linville if (crypt->ops) { 782ba4b32eSJohn W. Linville crypt->ops->deinit(crypt->priv); 792ba4b32eSJohn W. Linville module_put(crypt->ops->owner); 802ba4b32eSJohn W. Linville } 812ba4b32eSJohn W. Linville kfree(crypt); 822ba4b32eSJohn W. Linville info->crypt[i] = NULL; 832ba4b32eSJohn W. Linville } 842ba4b32eSJohn W. Linville } 852ba4b32eSJohn W. Linville } 862ba4b32eSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_info_free); 872ba4b32eSJohn W. Linville 889d630c77SPavel Roskin static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, 899d630c77SPavel Roskin int force) 907e272fcfSJohn W. Linville { 91274bfb8dSJohn W. Linville struct lib80211_crypt_data *entry, *next; 92274bfb8dSJohn W. Linville unsigned long flags; 93274bfb8dSJohn W. Linville 94274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 95274bfb8dSJohn W. Linville list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { 96274bfb8dSJohn W. Linville if (atomic_read(&entry->refcnt) != 0 && !force) 97274bfb8dSJohn W. Linville continue; 98274bfb8dSJohn W. Linville 99274bfb8dSJohn W. Linville list_del(&entry->list); 100274bfb8dSJohn W. Linville 101274bfb8dSJohn W. Linville if (entry->ops) { 102274bfb8dSJohn W. Linville entry->ops->deinit(entry->priv); 103274bfb8dSJohn W. Linville module_put(entry->ops->owner); 104274bfb8dSJohn W. Linville } 105274bfb8dSJohn W. Linville kfree(entry); 106274bfb8dSJohn W. Linville } 107274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 108274bfb8dSJohn W. Linville } 109274bfb8dSJohn W. Linville 110274bfb8dSJohn W. Linville /* After this, crypt_deinit_list won't accept new members */ 1119d630c77SPavel Roskin static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) 112274bfb8dSJohn W. Linville { 113274bfb8dSJohn W. Linville unsigned long flags; 114274bfb8dSJohn W. Linville 115274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 116274bfb8dSJohn W. Linville info->crypt_quiesced = 1; 117274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 118274bfb8dSJohn W. Linville } 119274bfb8dSJohn W. Linville 120e99e88a9SKees Cook static void lib80211_crypt_deinit_handler(struct timer_list *t) 121274bfb8dSJohn W. Linville { 122e99e88a9SKees Cook struct lib80211_crypt_info *info = from_timer(info, t, 123e99e88a9SKees Cook crypt_deinit_timer); 124274bfb8dSJohn W. Linville unsigned long flags; 125274bfb8dSJohn W. Linville 126274bfb8dSJohn W. Linville lib80211_crypt_deinit_entries(info, 0); 127274bfb8dSJohn W. Linville 128274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 129274bfb8dSJohn W. Linville if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { 130274bfb8dSJohn W. Linville printk(KERN_DEBUG "%s: entries remaining in delayed crypt " 131274bfb8dSJohn W. Linville "deletion list\n", info->name); 132274bfb8dSJohn W. Linville info->crypt_deinit_timer.expires = jiffies + HZ; 133274bfb8dSJohn W. Linville add_timer(&info->crypt_deinit_timer); 134274bfb8dSJohn W. Linville } 135274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 136274bfb8dSJohn W. Linville } 137274bfb8dSJohn W. Linville 138274bfb8dSJohn W. Linville void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, 139274bfb8dSJohn W. Linville struct lib80211_crypt_data **crypt) 140274bfb8dSJohn W. Linville { 141274bfb8dSJohn W. Linville struct lib80211_crypt_data *tmp; 142274bfb8dSJohn W. Linville unsigned long flags; 143274bfb8dSJohn W. Linville 144274bfb8dSJohn W. Linville if (*crypt == NULL) 145274bfb8dSJohn W. Linville return; 146274bfb8dSJohn W. Linville 147274bfb8dSJohn W. Linville tmp = *crypt; 148274bfb8dSJohn W. Linville *crypt = NULL; 149274bfb8dSJohn W. Linville 150274bfb8dSJohn W. Linville /* must not run ops->deinit() while there may be pending encrypt or 151274bfb8dSJohn W. Linville * decrypt operations. Use a list of delayed deinits to avoid needing 152274bfb8dSJohn W. Linville * locking. */ 153274bfb8dSJohn W. Linville 154274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 155274bfb8dSJohn W. Linville if (!info->crypt_quiesced) { 156274bfb8dSJohn W. Linville list_add(&tmp->list, &info->crypt_deinit_list); 157274bfb8dSJohn W. Linville if (!timer_pending(&info->crypt_deinit_timer)) { 158274bfb8dSJohn W. Linville info->crypt_deinit_timer.expires = jiffies + HZ; 159274bfb8dSJohn W. Linville add_timer(&info->crypt_deinit_timer); 160274bfb8dSJohn W. Linville } 161274bfb8dSJohn W. Linville } 162274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 163274bfb8dSJohn W. Linville } 164274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); 165274bfb8dSJohn W. Linville 166274bfb8dSJohn W. Linville int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) 167274bfb8dSJohn W. Linville { 168274bfb8dSJohn W. Linville unsigned long flags; 169274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 170274bfb8dSJohn W. Linville 171274bfb8dSJohn W. Linville alg = kzalloc(sizeof(*alg), GFP_KERNEL); 172274bfb8dSJohn W. Linville if (alg == NULL) 173274bfb8dSJohn W. Linville return -ENOMEM; 174274bfb8dSJohn W. Linville 175274bfb8dSJohn W. Linville alg->ops = ops; 176274bfb8dSJohn W. Linville 177274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 178274bfb8dSJohn W. Linville list_add(&alg->list, &lib80211_crypto_algs); 179274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 180274bfb8dSJohn W. Linville 181274bfb8dSJohn W. Linville printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", 182274bfb8dSJohn W. Linville ops->name); 183274bfb8dSJohn W. Linville 1847e272fcfSJohn W. Linville return 0; 1857e272fcfSJohn W. Linville } 186274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_register_crypto_ops); 1877e272fcfSJohn W. Linville 188274bfb8dSJohn W. Linville int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) 189274bfb8dSJohn W. Linville { 190274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 191274bfb8dSJohn W. Linville unsigned long flags; 192274bfb8dSJohn W. Linville 193274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 194274bfb8dSJohn W. Linville list_for_each_entry(alg, &lib80211_crypto_algs, list) { 195274bfb8dSJohn W. Linville if (alg->ops == ops) 196274bfb8dSJohn W. Linville goto found; 197274bfb8dSJohn W. Linville } 198274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 199274bfb8dSJohn W. Linville return -EINVAL; 200274bfb8dSJohn W. Linville 201274bfb8dSJohn W. Linville found: 202e9c0268fSJoe Perches printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", 203e9c0268fSJoe Perches ops->name); 204274bfb8dSJohn W. Linville list_del(&alg->list); 205274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 206274bfb8dSJohn W. Linville kfree(alg); 207274bfb8dSJohn W. Linville return 0; 208274bfb8dSJohn W. Linville } 209274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_unregister_crypto_ops); 210274bfb8dSJohn W. Linville 211274bfb8dSJohn W. Linville struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) 212274bfb8dSJohn W. Linville { 213274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 214274bfb8dSJohn W. Linville unsigned long flags; 215274bfb8dSJohn W. Linville 216274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 217274bfb8dSJohn W. Linville list_for_each_entry(alg, &lib80211_crypto_algs, list) { 218274bfb8dSJohn W. Linville if (strcmp(alg->ops->name, name) == 0) 219274bfb8dSJohn W. Linville goto found; 220274bfb8dSJohn W. Linville } 221274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 222274bfb8dSJohn W. Linville return NULL; 223274bfb8dSJohn W. Linville 224274bfb8dSJohn W. Linville found: 225274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 226274bfb8dSJohn W. Linville return alg->ops; 227274bfb8dSJohn W. Linville } 228274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_get_crypto_ops); 229274bfb8dSJohn W. Linville 230274bfb8dSJohn W. Linville static void *lib80211_crypt_null_init(int keyidx) 231274bfb8dSJohn W. Linville { 232274bfb8dSJohn W. Linville return (void *)1; 233274bfb8dSJohn W. Linville } 234274bfb8dSJohn W. Linville 235274bfb8dSJohn W. Linville static void lib80211_crypt_null_deinit(void *priv) 2367e272fcfSJohn W. Linville { 2377e272fcfSJohn W. Linville } 2387e272fcfSJohn W. Linville 239274bfb8dSJohn W. Linville static struct lib80211_crypto_ops lib80211_crypt_null = { 240274bfb8dSJohn W. Linville .name = "NULL", 241274bfb8dSJohn W. Linville .init = lib80211_crypt_null_init, 242274bfb8dSJohn W. Linville .deinit = lib80211_crypt_null_deinit, 243274bfb8dSJohn W. Linville .owner = THIS_MODULE, 244274bfb8dSJohn W. Linville }; 245274bfb8dSJohn W. Linville 246274bfb8dSJohn W. Linville static int __init lib80211_init(void) 247274bfb8dSJohn W. Linville { 248e9c0268fSJoe Perches pr_info(DRV_DESCRIPTION "\n"); 249274bfb8dSJohn W. Linville return lib80211_register_crypto_ops(&lib80211_crypt_null); 250274bfb8dSJohn W. Linville } 251274bfb8dSJohn W. Linville 252274bfb8dSJohn W. Linville static void __exit lib80211_exit(void) 253274bfb8dSJohn W. Linville { 254274bfb8dSJohn W. Linville lib80211_unregister_crypto_ops(&lib80211_crypt_null); 255274bfb8dSJohn W. Linville BUG_ON(!list_empty(&lib80211_crypto_algs)); 256274bfb8dSJohn W. Linville } 257274bfb8dSJohn W. Linville 258274bfb8dSJohn W. Linville module_init(lib80211_init); 259274bfb8dSJohn W. Linville module_exit(lib80211_exit); 260