Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * libipw -- common bits for IPW drivers
  4 *
  5 * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
  6 *
  7 * Portions copied from old ieee80211 component, w/ original copyright
  8 * notices below:
  9 *
 10 * Host AP crypto routines
 11 *
 12 * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 13 * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
 14 *
 15 */
 16
 17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 18
 19#include <linux/module.h>
 20#include <linux/ctype.h>
 21#include <linux/ieee80211.h>
 22#include <linux/errno.h>
 23#include <linux/init.h>
 24#include <linux/slab.h>
 25#include <linux/string.h>
 26#include "libipw.h"
 27
 28struct libipw_crypto_alg {
 29	struct list_head list;
 30	const struct libipw_crypto_ops *ops;
 31};
 32
 33static LIST_HEAD(libipw_crypto_algs);
 34static DEFINE_SPINLOCK(libipw_crypto_lock);
 35
 36static void libipw_crypt_deinit_entries(struct libipw_crypt_info *info,
 37					  int force);
 38static void libipw_crypt_quiescing(struct libipw_crypt_info *info);
 39static void libipw_crypt_deinit_handler(struct timer_list *t);
 40
 41int libipw_crypt_info_init(struct libipw_crypt_info *info, char *name,
 42				spinlock_t *lock)
 43{
 44	memset(info, 0, sizeof(*info));
 45
 46	info->name = name;
 47	info->lock = lock;
 48
 49	INIT_LIST_HEAD(&info->crypt_deinit_list);
 50	timer_setup(&info->crypt_deinit_timer, libipw_crypt_deinit_handler,
 51		    0);
 52
 53	return 0;
 54}
 55EXPORT_SYMBOL(libipw_crypt_info_init);
 56
 57void libipw_crypt_info_free(struct libipw_crypt_info *info)
 58{
 59	int i;
 60
 61        libipw_crypt_quiescing(info);
 62        del_timer_sync(&info->crypt_deinit_timer);
 63        libipw_crypt_deinit_entries(info, 1);
 64
 65        for (i = 0; i < NUM_WEP_KEYS; i++) {
 66                struct libipw_crypt_data *crypt = info->crypt[i];
 67                if (crypt) {
 68                        if (crypt->ops) {
 69                                crypt->ops->deinit(crypt->priv);
 70                                module_put(crypt->ops->owner);
 71                        }
 72                        kfree(crypt);
 73                        info->crypt[i] = NULL;
 74                }
 75        }
 76}
 77EXPORT_SYMBOL(libipw_crypt_info_free);
 78
 79static void libipw_crypt_deinit_entries(struct libipw_crypt_info *info,
 80					int force)
 81{
 82	struct libipw_crypt_data *entry, *next;
 83	unsigned long flags;
 84
 85	spin_lock_irqsave(info->lock, flags);
 86	list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
 87		if (atomic_read(&entry->refcnt) != 0 && !force)
 88			continue;
 89
 90		list_del(&entry->list);
 91
 92		if (entry->ops) {
 93			entry->ops->deinit(entry->priv);
 94			module_put(entry->ops->owner);
 95		}
 96		kfree(entry);
 97	}
 98	spin_unlock_irqrestore(info->lock, flags);
 99}
100
101/* After this, crypt_deinit_list won't accept new members */
102static void libipw_crypt_quiescing(struct libipw_crypt_info *info)
103{
104	unsigned long flags;
105
106	spin_lock_irqsave(info->lock, flags);
107	info->crypt_quiesced = 1;
108	spin_unlock_irqrestore(info->lock, flags);
109}
110
111static void libipw_crypt_deinit_handler(struct timer_list *t)
112{
113	struct libipw_crypt_info *info = from_timer(info, t,
114						    crypt_deinit_timer);
115	unsigned long flags;
116
117	libipw_crypt_deinit_entries(info, 0);
118
119	spin_lock_irqsave(info->lock, flags);
120	if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
121		printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
122		       "deletion list\n", info->name);
123		info->crypt_deinit_timer.expires = jiffies + HZ;
124		add_timer(&info->crypt_deinit_timer);
125	}
126	spin_unlock_irqrestore(info->lock, flags);
127}
128
129void libipw_crypt_delayed_deinit(struct libipw_crypt_info *info,
130				 struct libipw_crypt_data **crypt)
131{
132	struct libipw_crypt_data *tmp;
133	unsigned long flags;
134
135	if (*crypt == NULL)
136		return;
137
138	tmp = *crypt;
139	*crypt = NULL;
140
141	/* must not run ops->deinit() while there may be pending encrypt or
142	 * decrypt operations. Use a list of delayed deinits to avoid needing
143	 * locking. */
144
145	spin_lock_irqsave(info->lock, flags);
146	if (!info->crypt_quiesced) {
147		list_add(&tmp->list, &info->crypt_deinit_list);
148		if (!timer_pending(&info->crypt_deinit_timer)) {
149			info->crypt_deinit_timer.expires = jiffies + HZ;
150			add_timer(&info->crypt_deinit_timer);
151		}
152	}
153	spin_unlock_irqrestore(info->lock, flags);
154}
155EXPORT_SYMBOL(libipw_crypt_delayed_deinit);
156
157int libipw_register_crypto_ops(const struct libipw_crypto_ops *ops)
158{
159	unsigned long flags;
160	struct libipw_crypto_alg *alg;
161
162	alg = kzalloc(sizeof(*alg), GFP_KERNEL);
163	if (alg == NULL)
164		return -ENOMEM;
165
166	alg->ops = ops;
167
168	spin_lock_irqsave(&libipw_crypto_lock, flags);
169	list_add(&alg->list, &libipw_crypto_algs);
170	spin_unlock_irqrestore(&libipw_crypto_lock, flags);
171
172	printk(KERN_DEBUG "libipw_crypt: registered algorithm '%s'\n",
173	       ops->name);
174
175	return 0;
176}
177EXPORT_SYMBOL(libipw_register_crypto_ops);
178
179int libipw_unregister_crypto_ops(const struct libipw_crypto_ops *ops)
180{
181	struct libipw_crypto_alg *alg;
182	unsigned long flags;
183
184	spin_lock_irqsave(&libipw_crypto_lock, flags);
185	list_for_each_entry(alg, &libipw_crypto_algs, list) {
186		if (alg->ops == ops)
187			goto found;
188	}
189	spin_unlock_irqrestore(&libipw_crypto_lock, flags);
190	return -EINVAL;
191
192      found:
193	printk(KERN_DEBUG "libipw_crypt: unregistered algorithm '%s'\n",
194	       ops->name);
195	list_del(&alg->list);
196	spin_unlock_irqrestore(&libipw_crypto_lock, flags);
197	kfree(alg);
198	return 0;
199}
200EXPORT_SYMBOL(libipw_unregister_crypto_ops);
201
202const struct libipw_crypto_ops *libipw_get_crypto_ops(const char *name)
203{
204	struct libipw_crypto_alg *alg;
205	unsigned long flags;
206
207	spin_lock_irqsave(&libipw_crypto_lock, flags);
208	list_for_each_entry(alg, &libipw_crypto_algs, list) {
209		if (strcmp(alg->ops->name, name) == 0)
210			goto found;
211	}
212	spin_unlock_irqrestore(&libipw_crypto_lock, flags);
213	return NULL;
214
215      found:
216	spin_unlock_irqrestore(&libipw_crypto_lock, flags);
217	return alg->ops;
218}
219EXPORT_SYMBOL(libipw_get_crypto_ops);
220
221static void *libipw_crypt_null_init(int keyidx)
222{
223	return (void *)1;
224}
225
226static void libipw_crypt_null_deinit(void *priv)
227{
228}
229
230static const struct libipw_crypto_ops libipw_crypt_null = {
231	.name = "NULL",
232	.init = libipw_crypt_null_init,
233	.deinit = libipw_crypt_null_deinit,
234	.owner = THIS_MODULE,
235};
236
237int __init libipw_crypto_init(void)
238{
239	return libipw_register_crypto_ops(&libipw_crypt_null);
240}
241
242void libipw_crypto_exit(void)
243{
244	libipw_unregister_crypto_ops(&libipw_crypt_null);
245	BUG_ON(!list_empty(&libipw_crypto_algs));
246}