Merge branch 'next/deletion' of git://git.linaro.org/people/arnd/arm-soc
[pandora-kernel.git] / drivers / staging / rtl8192e / rtllib_crypt.c
1 /*
2  * Host AP crypto routines
3  *
4  * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
5  * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation. See README and COPYING for
10  * more details.
11  *
12  */
13
14 #include <linux/version.h>
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/slab.h>
18 #include <linux/string.h>
19 #include <linux/errno.h>
20
21 #include "rtllib.h"
22
23 struct rtllib_crypto_alg {
24         struct list_head list;
25         struct rtllib_crypto_ops *ops;
26 };
27
28
29 struct rtllib_crypto {
30         struct list_head algs;
31         spinlock_t lock;
32 };
33
34 static struct rtllib_crypto *hcrypt;
35
36 void rtllib_crypt_deinit_entries(struct rtllib_device *ieee,
37                                            int force)
38 {
39         struct list_head *ptr, *n;
40         struct rtllib_crypt_data *entry;
41
42         for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
43              ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
44                 entry = list_entry(ptr, struct rtllib_crypt_data, list);
45
46                 if (atomic_read(&entry->refcnt) != 0 && !force)
47                         continue;
48
49                 list_del(ptr);
50
51                 if (entry->ops)
52                         entry->ops->deinit(entry->priv);
53                 kfree(entry);
54         }
55 }
56
57 void rtllib_crypt_deinit_handler(unsigned long data)
58 {
59         struct rtllib_device *ieee = (struct rtllib_device *)data;
60         unsigned long flags;
61
62         spin_lock_irqsave(&ieee->lock, flags);
63         rtllib_crypt_deinit_entries(ieee, 0);
64         if (!list_empty(&ieee->crypt_deinit_list)) {
65                 printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
66                        "deletion list\n", ieee->dev->name);
67                 ieee->crypt_deinit_timer.expires = jiffies + HZ;
68                 add_timer(&ieee->crypt_deinit_timer);
69         }
70         spin_unlock_irqrestore(&ieee->lock, flags);
71
72 }
73
74 void rtllib_crypt_delayed_deinit(struct rtllib_device *ieee,
75                                     struct rtllib_crypt_data **crypt)
76 {
77         struct rtllib_crypt_data *tmp;
78         unsigned long flags;
79
80         if (*crypt == NULL)
81                 return;
82
83         tmp = *crypt;
84         *crypt = NULL;
85
86         /* must not run ops->deinit() while there may be pending encrypt or
87          * decrypt operations. Use a list of delayed deinits to avoid needing
88          * locking. */
89
90         spin_lock_irqsave(&ieee->lock, flags);
91         list_add(&tmp->list, &ieee->crypt_deinit_list);
92         if (!timer_pending(&ieee->crypt_deinit_timer)) {
93                 ieee->crypt_deinit_timer.expires = jiffies + HZ;
94                 add_timer(&ieee->crypt_deinit_timer);
95         }
96         spin_unlock_irqrestore(&ieee->lock, flags);
97 }
98
99 int rtllib_register_crypto_ops(struct rtllib_crypto_ops *ops)
100 {
101         unsigned long flags;
102         struct rtllib_crypto_alg *alg;
103
104         if (hcrypt == NULL)
105                 return -1;
106
107         alg = kmalloc(sizeof(*alg), GFP_KERNEL);
108         if (alg == NULL)
109                 return -ENOMEM;
110
111         memset(alg, 0, sizeof(*alg));
112         alg->ops = ops;
113
114         spin_lock_irqsave(&hcrypt->lock, flags);
115         list_add(&alg->list, &hcrypt->algs);
116         spin_unlock_irqrestore(&hcrypt->lock, flags);
117
118         printk(KERN_DEBUG "rtllib_crypt: registered algorithm '%s'\n",
119                ops->name);
120
121         return 0;
122 }
123
124 int rtllib_unregister_crypto_ops(struct rtllib_crypto_ops *ops)
125 {
126         unsigned long flags;
127         struct list_head *ptr;
128         struct rtllib_crypto_alg *del_alg = NULL;
129
130         if (hcrypt == NULL)
131                 return -1;
132
133         spin_lock_irqsave(&hcrypt->lock, flags);
134         for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
135                 struct rtllib_crypto_alg *alg =
136                         (struct rtllib_crypto_alg *) ptr;
137                 if (alg->ops == ops) {
138                         list_del(&alg->list);
139                         del_alg = alg;
140                         break;
141                 }
142         }
143         spin_unlock_irqrestore(&hcrypt->lock, flags);
144
145         if (del_alg) {
146                 printk(KERN_DEBUG "rtllib_crypt: unregistered algorithm "
147                        "'%s'\n", ops->name);
148                 kfree(del_alg);
149         }
150
151         return del_alg ? 0 : -1;
152 }
153
154
155 struct rtllib_crypto_ops *rtllib_get_crypto_ops(const char *name)
156 {
157         unsigned long flags;
158         struct list_head *ptr;
159         struct rtllib_crypto_alg *found_alg = NULL;
160
161         if (hcrypt == NULL)
162                 return NULL;
163
164         spin_lock_irqsave(&hcrypt->lock, flags);
165         for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
166                 struct rtllib_crypto_alg *alg =
167                         (struct rtllib_crypto_alg *) ptr;
168                 if (strcmp(alg->ops->name, name) == 0) {
169                         found_alg = alg;
170                         break;
171                 }
172         }
173         spin_unlock_irqrestore(&hcrypt->lock, flags);
174
175         if (found_alg)
176                 return found_alg->ops;
177         else
178                 return NULL;
179 }
180
181
182 static void * rtllib_crypt_null_init(int keyidx) { return (void *) 1; }
183 static void rtllib_crypt_null_deinit(void *priv) {}
184
185 static struct rtllib_crypto_ops rtllib_crypt_null = {
186         .name                   = "NULL",
187         .init                   = rtllib_crypt_null_init,
188         .deinit                 = rtllib_crypt_null_deinit,
189         .encrypt_mpdu           = NULL,
190         .decrypt_mpdu           = NULL,
191         .encrypt_msdu           = NULL,
192         .decrypt_msdu           = NULL,
193         .set_key                = NULL,
194         .get_key                = NULL,
195         .extra_prefix_len       = 0,
196         .extra_postfix_len      = 0,
197         .owner                  = THIS_MODULE,
198 };
199
200
201 int __init rtllib_crypto_init(void)
202 {
203         int ret = -ENOMEM;
204
205         hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL);
206         if (!hcrypt)
207                 goto out;
208
209         memset(hcrypt, 0, sizeof(*hcrypt));
210         INIT_LIST_HEAD(&hcrypt->algs);
211         spin_lock_init(&hcrypt->lock);
212
213         ret = rtllib_register_crypto_ops(&rtllib_crypt_null);
214         if (ret < 0) {
215                 kfree(hcrypt);
216                 hcrypt = NULL;
217         }
218 out:
219         return ret;
220 }
221
222
223 void __exit rtllib_crypto_deinit(void)
224 {
225         struct list_head *ptr, *n;
226
227         if (hcrypt == NULL)
228                 return;
229
230         for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
231              ptr = n, n = ptr->next) {
232                 struct rtllib_crypto_alg *alg =
233                         (struct rtllib_crypto_alg *) ptr;
234                 list_del(ptr);
235                 printk(KERN_DEBUG "rtllib_crypt: unregistered algorithm "
236                        "'%s' (deinit)\n", alg->ops->name);
237                 kfree(alg);
238         }
239
240         kfree(hcrypt);
241 }