17e272fcfSJohn W. Linville /* 27e272fcfSJohn W. Linville * lib80211 -- common bits for IEEE802.11 drivers 37e272fcfSJohn W. Linville * 47e272fcfSJohn W. Linville * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com> 57e272fcfSJohn W. Linville * 6274bfb8dSJohn W. Linville * Portions copied from old ieee80211 component, w/ original copyright 7274bfb8dSJohn W. Linville * notices below: 8274bfb8dSJohn W. Linville * 9274bfb8dSJohn W. Linville * Host AP crypto routines 10274bfb8dSJohn W. Linville * 11274bfb8dSJohn W. Linville * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> 12274bfb8dSJohn W. Linville * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> 13274bfb8dSJohn W. Linville * 147e272fcfSJohn W. Linville */ 157e272fcfSJohn W. Linville 16e9c0268fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17e9c0268fSJoe Perches 187e272fcfSJohn W. Linville #include <linux/module.h> 192819f8adSJohn W. Linville #include <linux/ctype.h> 207e272fcfSJohn W. Linville #include <linux/ieee80211.h> 21274bfb8dSJohn W. Linville #include <linux/errno.h> 22274bfb8dSJohn W. Linville #include <linux/init.h> 23274bfb8dSJohn W. Linville #include <linux/slab.h> 24274bfb8dSJohn W. Linville #include <linux/string.h> 257e272fcfSJohn W. Linville 267e272fcfSJohn W. Linville #include <net/lib80211.h> 277e272fcfSJohn W. Linville 287e272fcfSJohn W. Linville #define DRV_NAME "lib80211" 297e272fcfSJohn W. Linville 307e272fcfSJohn W. Linville #define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" 317e272fcfSJohn W. Linville 327e272fcfSJohn W. Linville MODULE_DESCRIPTION(DRV_DESCRIPTION); 337e272fcfSJohn W. Linville MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>"); 347e272fcfSJohn W. Linville MODULE_LICENSE("GPL"); 357e272fcfSJohn W. Linville 36274bfb8dSJohn W. Linville struct lib80211_crypto_alg { 37274bfb8dSJohn W. Linville struct list_head list; 38274bfb8dSJohn W. Linville struct lib80211_crypto_ops *ops; 39274bfb8dSJohn W. Linville }; 40274bfb8dSJohn W. Linville 41274bfb8dSJohn W. Linville static LIST_HEAD(lib80211_crypto_algs); 42274bfb8dSJohn W. Linville static DEFINE_SPINLOCK(lib80211_crypto_lock); 43274bfb8dSJohn W. Linville 44*9d630c77SPavel Roskin static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, 45*9d630c77SPavel Roskin int force); 46*9d630c77SPavel Roskin static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); 47*9d630c77SPavel Roskin static void lib80211_crypt_deinit_handler(unsigned long data); 48*9d630c77SPavel Roskin 499387b7caSJohn W. Linville const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) 507e272fcfSJohn W. Linville { 517e272fcfSJohn W. Linville const char *s = ssid; 529387b7caSJohn W. Linville char *d = buf; 537e272fcfSJohn W. Linville 547e272fcfSJohn W. Linville ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); 557e272fcfSJohn W. Linville while (ssid_len--) { 562819f8adSJohn W. Linville if (isprint(*s)) { 577e272fcfSJohn W. Linville *d++ = *s++; 582819f8adSJohn W. Linville continue; 597e272fcfSJohn W. Linville } 602819f8adSJohn W. Linville 612819f8adSJohn W. Linville *d++ = '\\'; 622819f8adSJohn W. Linville if (*s == '\0') 632819f8adSJohn W. Linville *d++ = '0'; 642819f8adSJohn W. Linville else if (*s == '\n') 652819f8adSJohn W. Linville *d++ = 'n'; 662819f8adSJohn W. Linville else if (*s == '\r') 672819f8adSJohn W. Linville *d++ = 'r'; 682819f8adSJohn W. Linville else if (*s == '\t') 692819f8adSJohn W. Linville *d++ = 't'; 702819f8adSJohn W. Linville else if (*s == '\\') 712819f8adSJohn W. Linville *d++ = '\\'; 722819f8adSJohn W. Linville else 732819f8adSJohn W. Linville d += snprintf(d, 3, "%03o", *s); 742819f8adSJohn W. Linville s++; 757e272fcfSJohn W. Linville } 767e272fcfSJohn W. Linville *d = '\0'; 779387b7caSJohn W. Linville return buf; 787e272fcfSJohn W. Linville } 799387b7caSJohn W. Linville EXPORT_SYMBOL(print_ssid); 807e272fcfSJohn W. Linville 812ba4b32eSJohn W. Linville int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, 822ba4b32eSJohn W. Linville spinlock_t *lock) 832ba4b32eSJohn W. Linville { 842ba4b32eSJohn W. Linville memset(info, 0, sizeof(*info)); 852ba4b32eSJohn W. Linville 862ba4b32eSJohn W. Linville info->name = name; 872ba4b32eSJohn W. Linville info->lock = lock; 882ba4b32eSJohn W. Linville 892ba4b32eSJohn W. Linville INIT_LIST_HEAD(&info->crypt_deinit_list); 902ba4b32eSJohn W. Linville setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, 912ba4b32eSJohn W. Linville (unsigned long)info); 922ba4b32eSJohn W. Linville 932ba4b32eSJohn W. Linville return 0; 942ba4b32eSJohn W. Linville } 952ba4b32eSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_info_init); 962ba4b32eSJohn W. Linville 972ba4b32eSJohn W. Linville void lib80211_crypt_info_free(struct lib80211_crypt_info *info) 982ba4b32eSJohn W. Linville { 992ba4b32eSJohn W. Linville int i; 1002ba4b32eSJohn W. Linville 1012ba4b32eSJohn W. Linville lib80211_crypt_quiescing(info); 1022ba4b32eSJohn W. Linville del_timer_sync(&info->crypt_deinit_timer); 1032ba4b32eSJohn W. Linville lib80211_crypt_deinit_entries(info, 1); 1042ba4b32eSJohn W. Linville 1052ba4b32eSJohn W. Linville for (i = 0; i < NUM_WEP_KEYS; i++) { 1062ba4b32eSJohn W. Linville struct lib80211_crypt_data *crypt = info->crypt[i]; 1072ba4b32eSJohn W. Linville if (crypt) { 1082ba4b32eSJohn W. Linville if (crypt->ops) { 1092ba4b32eSJohn W. Linville crypt->ops->deinit(crypt->priv); 1102ba4b32eSJohn W. Linville module_put(crypt->ops->owner); 1112ba4b32eSJohn W. Linville } 1122ba4b32eSJohn W. Linville kfree(crypt); 1132ba4b32eSJohn W. Linville info->crypt[i] = NULL; 1142ba4b32eSJohn W. Linville } 1152ba4b32eSJohn W. Linville } 1162ba4b32eSJohn W. Linville } 1172ba4b32eSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_info_free); 1182ba4b32eSJohn W. Linville 119*9d630c77SPavel Roskin static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, 120*9d630c77SPavel Roskin int force) 1217e272fcfSJohn W. Linville { 122274bfb8dSJohn W. Linville struct lib80211_crypt_data *entry, *next; 123274bfb8dSJohn W. Linville unsigned long flags; 124274bfb8dSJohn W. Linville 125274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 126274bfb8dSJohn W. Linville list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { 127274bfb8dSJohn W. Linville if (atomic_read(&entry->refcnt) != 0 && !force) 128274bfb8dSJohn W. Linville continue; 129274bfb8dSJohn W. Linville 130274bfb8dSJohn W. Linville list_del(&entry->list); 131274bfb8dSJohn W. Linville 132274bfb8dSJohn W. Linville if (entry->ops) { 133274bfb8dSJohn W. Linville entry->ops->deinit(entry->priv); 134274bfb8dSJohn W. Linville module_put(entry->ops->owner); 135274bfb8dSJohn W. Linville } 136274bfb8dSJohn W. Linville kfree(entry); 137274bfb8dSJohn W. Linville } 138274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 139274bfb8dSJohn W. Linville } 140274bfb8dSJohn W. Linville 141274bfb8dSJohn W. Linville /* After this, crypt_deinit_list won't accept new members */ 142*9d630c77SPavel Roskin static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) 143274bfb8dSJohn W. Linville { 144274bfb8dSJohn W. Linville unsigned long flags; 145274bfb8dSJohn W. Linville 146274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 147274bfb8dSJohn W. Linville info->crypt_quiesced = 1; 148274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 149274bfb8dSJohn W. Linville } 150274bfb8dSJohn W. Linville 151*9d630c77SPavel Roskin static void lib80211_crypt_deinit_handler(unsigned long data) 152274bfb8dSJohn W. Linville { 153274bfb8dSJohn W. Linville struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; 154274bfb8dSJohn W. Linville unsigned long flags; 155274bfb8dSJohn W. Linville 156274bfb8dSJohn W. Linville lib80211_crypt_deinit_entries(info, 0); 157274bfb8dSJohn W. Linville 158274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 159274bfb8dSJohn W. Linville if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { 160274bfb8dSJohn W. Linville printk(KERN_DEBUG "%s: entries remaining in delayed crypt " 161274bfb8dSJohn W. Linville "deletion list\n", info->name); 162274bfb8dSJohn W. Linville info->crypt_deinit_timer.expires = jiffies + HZ; 163274bfb8dSJohn W. Linville add_timer(&info->crypt_deinit_timer); 164274bfb8dSJohn W. Linville } 165274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 166274bfb8dSJohn W. Linville } 167274bfb8dSJohn W. Linville 168274bfb8dSJohn W. Linville void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, 169274bfb8dSJohn W. Linville struct lib80211_crypt_data **crypt) 170274bfb8dSJohn W. Linville { 171274bfb8dSJohn W. Linville struct lib80211_crypt_data *tmp; 172274bfb8dSJohn W. Linville unsigned long flags; 173274bfb8dSJohn W. Linville 174274bfb8dSJohn W. Linville if (*crypt == NULL) 175274bfb8dSJohn W. Linville return; 176274bfb8dSJohn W. Linville 177274bfb8dSJohn W. Linville tmp = *crypt; 178274bfb8dSJohn W. Linville *crypt = NULL; 179274bfb8dSJohn W. Linville 180274bfb8dSJohn W. Linville /* must not run ops->deinit() while there may be pending encrypt or 181274bfb8dSJohn W. Linville * decrypt operations. Use a list of delayed deinits to avoid needing 182274bfb8dSJohn W. Linville * locking. */ 183274bfb8dSJohn W. Linville 184274bfb8dSJohn W. Linville spin_lock_irqsave(info->lock, flags); 185274bfb8dSJohn W. Linville if (!info->crypt_quiesced) { 186274bfb8dSJohn W. Linville list_add(&tmp->list, &info->crypt_deinit_list); 187274bfb8dSJohn W. Linville if (!timer_pending(&info->crypt_deinit_timer)) { 188274bfb8dSJohn W. Linville info->crypt_deinit_timer.expires = jiffies + HZ; 189274bfb8dSJohn W. Linville add_timer(&info->crypt_deinit_timer); 190274bfb8dSJohn W. Linville } 191274bfb8dSJohn W. Linville } 192274bfb8dSJohn W. Linville spin_unlock_irqrestore(info->lock, flags); 193274bfb8dSJohn W. Linville } 194274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); 195274bfb8dSJohn W. Linville 196274bfb8dSJohn W. Linville int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) 197274bfb8dSJohn W. Linville { 198274bfb8dSJohn W. Linville unsigned long flags; 199274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 200274bfb8dSJohn W. Linville 201274bfb8dSJohn W. Linville alg = kzalloc(sizeof(*alg), GFP_KERNEL); 202274bfb8dSJohn W. Linville if (alg == NULL) 203274bfb8dSJohn W. Linville return -ENOMEM; 204274bfb8dSJohn W. Linville 205274bfb8dSJohn W. Linville alg->ops = ops; 206274bfb8dSJohn W. Linville 207274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 208274bfb8dSJohn W. Linville list_add(&alg->list, &lib80211_crypto_algs); 209274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 210274bfb8dSJohn W. Linville 211274bfb8dSJohn W. Linville printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", 212274bfb8dSJohn W. Linville ops->name); 213274bfb8dSJohn W. Linville 2147e272fcfSJohn W. Linville return 0; 2157e272fcfSJohn W. Linville } 216274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_register_crypto_ops); 2177e272fcfSJohn W. Linville 218274bfb8dSJohn W. Linville int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) 219274bfb8dSJohn W. Linville { 220274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 221274bfb8dSJohn W. Linville unsigned long flags; 222274bfb8dSJohn W. Linville 223274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 224274bfb8dSJohn W. Linville list_for_each_entry(alg, &lib80211_crypto_algs, list) { 225274bfb8dSJohn W. Linville if (alg->ops == ops) 226274bfb8dSJohn W. Linville goto found; 227274bfb8dSJohn W. Linville } 228274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 229274bfb8dSJohn W. Linville return -EINVAL; 230274bfb8dSJohn W. Linville 231274bfb8dSJohn W. Linville found: 232e9c0268fSJoe Perches printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", 233e9c0268fSJoe Perches ops->name); 234274bfb8dSJohn W. Linville list_del(&alg->list); 235274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 236274bfb8dSJohn W. Linville kfree(alg); 237274bfb8dSJohn W. Linville return 0; 238274bfb8dSJohn W. Linville } 239274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_unregister_crypto_ops); 240274bfb8dSJohn W. Linville 241274bfb8dSJohn W. Linville struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) 242274bfb8dSJohn W. Linville { 243274bfb8dSJohn W. Linville struct lib80211_crypto_alg *alg; 244274bfb8dSJohn W. Linville unsigned long flags; 245274bfb8dSJohn W. Linville 246274bfb8dSJohn W. Linville spin_lock_irqsave(&lib80211_crypto_lock, flags); 247274bfb8dSJohn W. Linville list_for_each_entry(alg, &lib80211_crypto_algs, list) { 248274bfb8dSJohn W. Linville if (strcmp(alg->ops->name, name) == 0) 249274bfb8dSJohn W. Linville goto found; 250274bfb8dSJohn W. Linville } 251274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 252274bfb8dSJohn W. Linville return NULL; 253274bfb8dSJohn W. Linville 254274bfb8dSJohn W. Linville found: 255274bfb8dSJohn W. Linville spin_unlock_irqrestore(&lib80211_crypto_lock, flags); 256274bfb8dSJohn W. Linville return alg->ops; 257274bfb8dSJohn W. Linville } 258274bfb8dSJohn W. Linville EXPORT_SYMBOL(lib80211_get_crypto_ops); 259274bfb8dSJohn W. Linville 260274bfb8dSJohn W. Linville static void *lib80211_crypt_null_init(int keyidx) 261274bfb8dSJohn W. Linville { 262274bfb8dSJohn W. Linville return (void *)1; 263274bfb8dSJohn W. Linville } 264274bfb8dSJohn W. Linville 265274bfb8dSJohn W. Linville static void lib80211_crypt_null_deinit(void *priv) 2667e272fcfSJohn W. Linville { 2677e272fcfSJohn W. Linville } 2687e272fcfSJohn W. Linville 269274bfb8dSJohn W. Linville static struct lib80211_crypto_ops lib80211_crypt_null = { 270274bfb8dSJohn W. Linville .name = "NULL", 271274bfb8dSJohn W. Linville .init = lib80211_crypt_null_init, 272274bfb8dSJohn W. Linville .deinit = lib80211_crypt_null_deinit, 273274bfb8dSJohn W. Linville .owner = THIS_MODULE, 274274bfb8dSJohn W. Linville }; 275274bfb8dSJohn W. Linville 276274bfb8dSJohn W. Linville static int __init lib80211_init(void) 277274bfb8dSJohn W. Linville { 278e9c0268fSJoe Perches pr_info(DRV_DESCRIPTION "\n"); 279274bfb8dSJohn W. Linville return lib80211_register_crypto_ops(&lib80211_crypt_null); 280274bfb8dSJohn W. Linville } 281274bfb8dSJohn W. Linville 282274bfb8dSJohn W. Linville static void __exit lib80211_exit(void) 283274bfb8dSJohn W. Linville { 284274bfb8dSJohn W. Linville lib80211_unregister_crypto_ops(&lib80211_crypt_null); 285274bfb8dSJohn W. Linville BUG_ON(!list_empty(&lib80211_crypto_algs)); 286274bfb8dSJohn W. Linville } 287274bfb8dSJohn W. Linville 288274bfb8dSJohn W. Linville module_init(lib80211_init); 289274bfb8dSJohn W. Linville module_exit(lib80211_exit); 290