Blame view

net/wireless/lib80211.c 6.55 KB
7e272fcff   John W. Linville   wireless: consoli...
1
2
3
4
5
  /*
   * lib80211 -- common bits for IEEE802.11 drivers
   *
   * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
   *
274bfb8dc   John W. Linville   lib80211: absorb ...
6
7
8
9
10
11
12
13
   * Portions copied from old ieee80211 component, w/ original copyright
   * notices below:
   *
   * Host AP crypto routines
   *
   * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
   * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
   *
7e272fcff   John W. Linville   wireless: consoli...
14
   */
e9c0268f0   Joe Perches   net/wireless: Use...
15
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7e272fcff   John W. Linville   wireless: consoli...
16
  #include <linux/module.h>
2819f8ad6   John W. Linville   wireless: escape_...
17
  #include <linux/ctype.h>
7e272fcff   John W. Linville   wireless: consoli...
18
  #include <linux/ieee80211.h>
274bfb8dc   John W. Linville   lib80211: absorb ...
19
20
21
22
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/string.h>
7e272fcff   John W. Linville   wireless: consoli...
23
24
25
26
27
28
29
30
31
32
  
  #include <net/lib80211.h>
  
  #define DRV_NAME        "lib80211"
  
  #define DRV_DESCRIPTION	"common routines for IEEE802.11 drivers"
  
  MODULE_DESCRIPTION(DRV_DESCRIPTION);
  MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
  MODULE_LICENSE("GPL");
274bfb8dc   John W. Linville   lib80211: absorb ...
33
34
35
36
37
38
39
  struct lib80211_crypto_alg {
  	struct list_head list;
  	struct lib80211_crypto_ops *ops;
  };
  
  static LIST_HEAD(lib80211_crypto_algs);
  static DEFINE_SPINLOCK(lib80211_crypto_lock);
9d630c779   Pavel Roskin   lib80211: remove ...
40
41
42
43
  static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
  					  int force);
  static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
  static void lib80211_crypt_deinit_handler(unsigned long data);
2ba4b32ec   John W. Linville   lib80211: consoli...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
  				spinlock_t *lock)
  {
  	memset(info, 0, sizeof(*info));
  
  	info->name = name;
  	info->lock = lock;
  
  	INIT_LIST_HEAD(&info->crypt_deinit_list);
  	setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
  			(unsigned long)info);
  
  	return 0;
  }
  EXPORT_SYMBOL(lib80211_crypt_info_init);
  
  void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
  {
  	int i;
  
          lib80211_crypt_quiescing(info);
          del_timer_sync(&info->crypt_deinit_timer);
          lib80211_crypt_deinit_entries(info, 1);
  
          for (i = 0; i < NUM_WEP_KEYS; i++) {
                  struct lib80211_crypt_data *crypt = info->crypt[i];
                  if (crypt) {
                          if (crypt->ops) {
                                  crypt->ops->deinit(crypt->priv);
                                  module_put(crypt->ops->owner);
                          }
                          kfree(crypt);
                          info->crypt[i] = NULL;
                  }
          }
  }
  EXPORT_SYMBOL(lib80211_crypt_info_free);
9d630c779   Pavel Roskin   lib80211: remove ...
81
82
  static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
  					  int force)
7e272fcff   John W. Linville   wireless: consoli...
83
  {
274bfb8dc   John W. Linville   lib80211: absorb ...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  	struct lib80211_crypt_data *entry, *next;
  	unsigned long flags;
  
  	spin_lock_irqsave(info->lock, flags);
  	list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
  		if (atomic_read(&entry->refcnt) != 0 && !force)
  			continue;
  
  		list_del(&entry->list);
  
  		if (entry->ops) {
  			entry->ops->deinit(entry->priv);
  			module_put(entry->ops->owner);
  		}
  		kfree(entry);
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
102
103
  
  /* After this, crypt_deinit_list won't accept new members */
9d630c779   Pavel Roskin   lib80211: remove ...
104
  static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
274bfb8dc   John W. Linville   lib80211: absorb ...
105
106
107
108
109
110
111
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(info->lock, flags);
  	info->crypt_quiesced = 1;
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
112

9d630c779   Pavel Roskin   lib80211: remove ...
113
  static void lib80211_crypt_deinit_handler(unsigned long data)
274bfb8dc   John W. Linville   lib80211: absorb ...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  {
  	struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
  	unsigned long flags;
  
  	lib80211_crypt_deinit_entries(info, 0);
  
  	spin_lock_irqsave(info->lock, flags);
  	if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
  		printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
  		       "deletion list
  ", info->name);
  		info->crypt_deinit_timer.expires = jiffies + HZ;
  		add_timer(&info->crypt_deinit_timer);
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  
  void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
  				    struct lib80211_crypt_data **crypt)
  {
  	struct lib80211_crypt_data *tmp;
  	unsigned long flags;
  
  	if (*crypt == NULL)
  		return;
  
  	tmp = *crypt;
  	*crypt = NULL;
  
  	/* must not run ops->deinit() while there may be pending encrypt or
  	 * decrypt operations. Use a list of delayed deinits to avoid needing
  	 * locking. */
  
  	spin_lock_irqsave(info->lock, flags);
  	if (!info->crypt_quiesced) {
  		list_add(&tmp->list, &info->crypt_deinit_list);
  		if (!timer_pending(&info->crypt_deinit_timer)) {
  			info->crypt_deinit_timer.expires = jiffies + HZ;
  			add_timer(&info->crypt_deinit_timer);
  		}
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
  EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
  
  int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
  {
  	unsigned long flags;
  	struct lib80211_crypto_alg *alg;
  
  	alg = kzalloc(sizeof(*alg), GFP_KERNEL);
  	if (alg == NULL)
  		return -ENOMEM;
  
  	alg->ops = ops;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_add(&alg->list, &lib80211_crypto_algs);
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  
  	printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'
  ",
  	       ops->name);
  
  	return 0;
  }
  EXPORT_SYMBOL(lib80211_register_crypto_ops);
  
  int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
  {
  	struct lib80211_crypto_alg *alg;
  	unsigned long flags;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
  		if (alg->ops == ops)
  			goto found;
  	}
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return -EINVAL;
  
        found:
e9c0268f0   Joe Perches   net/wireless: Use...
196
197
198
  	printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'
  ",
  	       ops->name);
274bfb8dc   John W. Linville   lib80211: absorb ...
199
200
201
  	list_del(&alg->list);
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	kfree(alg);
7e272fcff   John W. Linville   wireless: consoli...
202
203
  	return 0;
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
  
  struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
  {
  	struct lib80211_crypto_alg *alg;
  	unsigned long flags;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
  		if (strcmp(alg->ops->name, name) == 0)
  			goto found;
  	}
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return NULL;
  
        found:
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return alg->ops;
  }
  EXPORT_SYMBOL(lib80211_get_crypto_ops);
  
  static void *lib80211_crypt_null_init(int keyidx)
  {
  	return (void *)1;
  }
  
  static void lib80211_crypt_null_deinit(void *priv)
  {
  }
  
  static struct lib80211_crypto_ops lib80211_crypt_null = {
  	.name = "NULL",
  	.init = lib80211_crypt_null_init,
  	.deinit = lib80211_crypt_null_deinit,
  	.owner = THIS_MODULE,
  };
  
  static int __init lib80211_init(void)
  {
e9c0268f0   Joe Perches   net/wireless: Use...
243
244
  	pr_info(DRV_DESCRIPTION "
  ");
274bfb8dc   John W. Linville   lib80211: absorb ...
245
246
  	return lib80211_register_crypto_ops(&lib80211_crypt_null);
  }
7e272fcff   John W. Linville   wireless: consoli...
247

274bfb8dc   John W. Linville   lib80211: absorb ...
248
  static void __exit lib80211_exit(void)
7e272fcff   John W. Linville   wireless: consoli...
249
  {
274bfb8dc   John W. Linville   lib80211: absorb ...
250
251
  	lib80211_unregister_crypto_ops(&lib80211_crypt_null);
  	BUG_ON(!list_empty(&lib80211_crypto_algs));
7e272fcff   John W. Linville   wireless: consoli...
252
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
253
254
  module_init(lib80211_init);
  module_exit(lib80211_exit);