net: wireless: bcmdhd: Fix memleak in dev->p
[linux-2.6.git] / drivers / net / wireless / bcmdhd / bcmsdh_sdmmc_linux.c
1 /*
2  * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3  *
4  * Copyright (C) 1999-2011, Broadcom Corporation
5  * 
6  *         Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  * 
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  * 
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  * $Id: bcmsdh_sdmmc_linux.c,v 1.8.6.2 2011-02-01 18:38:36 Exp $
25  */
26
27 #include <typedefs.h>
28 #include <bcmutils.h>
29 #include <sdio.h>       /* SDIO Specs */
30 #include <bcmsdbus.h>   /* bcmsdh to/from specific controller APIs */
31 #include <sdiovar.h>    /* to get msglevel bit values */
32
33 #include <linux/sched.h>        /* request_irq() */
34
35 #include <linux/mmc/core.h>
36 #include <linux/mmc/card.h>
37 #include <linux/mmc/sdio_func.h>
38 #include <linux/mmc/sdio_ids.h>
39
40 #if !defined(SDIO_VENDOR_ID_BROADCOM)
41 #define SDIO_VENDOR_ID_BROADCOM         0x02d0
42 #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
43
44 #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
45
46 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
47 #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB      0x0492  /* BCM94325SDGWB */
48 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
49 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
50 #define SDIO_DEVICE_ID_BROADCOM_4325    0x0493
51 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
52 #if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
53 #define SDIO_DEVICE_ID_BROADCOM_4329    0x4329
54 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
55 #if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
56 #define SDIO_DEVICE_ID_BROADCOM_4319    0x4319
57 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */
58
59 #include <bcmsdh_sdmmc.h>
60
61 #include <dhd_dbg.h>
62
63 #ifdef WL_CFG80211
64 extern void wl_cfg80211_set_sdio_func(void *func);
65 #endif
66
67 extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
68 extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
69
70 int sdio_function_init(void);
71 void sdio_function_cleanup(void);
72
73 #define DESCRIPTION "bcmsdh_sdmmc Driver"
74 #define AUTHOR "Broadcom Corporation"
75
76 /* module param defaults */
77 static int clockoverride = 0;
78
79 module_param(clockoverride, int, 0644);
80 MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
81
82 PBCMSDH_SDMMC_INSTANCE gInstance;
83
84 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
85 #define BCMSDH_SDMMC_MAX_DEVICES 1
86
87 extern int bcmsdh_probe(struct device *dev);
88 extern int bcmsdh_remove(struct device *dev);
89
90 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
91                               const struct sdio_device_id *id)
92 {
93         int ret = 0;
94         static struct sdio_func sdio_func_0;
95         sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
96         sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
97         sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
98         sd_trace(("sdio_device: 0x%04x\n", func->device));
99         sd_trace(("Function#: 0x%04x\n", func->num));
100
101         if (func->num == 1) {
102                 sdio_func_0.num = 0;
103                 sdio_func_0.card = func->card;
104                 gInstance->func[0] = &sdio_func_0;
105                 if(func->device == 0x4) { /* 4318 */
106                         gInstance->func[2] = NULL;
107                         sd_trace(("NIC found, calling bcmsdh_probe...\n"));
108                         ret = bcmsdh_probe(&func->dev);
109                 }
110         }
111
112         gInstance->func[func->num] = func;
113
114         if (func->num == 2) {
115 #ifdef WL_CFG80211
116                 wl_cfg80211_set_sdio_func(func);
117 #endif
118                 sd_trace(("F2 found, calling bcmsdh_probe...\n"));
119                 ret = bcmsdh_probe(&func->dev);
120         }
121
122         return ret;
123 }
124
125 static void bcmsdh_sdmmc_remove(struct sdio_func *func)
126 {
127         sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
128         sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
129         sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
130         sd_info(("sdio_device: 0x%04x\n", func->device));
131         sd_info(("Function#: 0x%04x\n", func->num));
132
133         if (func->num == 2) {
134                 sd_trace(("F2 found, calling bcmsdh_remove...\n"));
135                 bcmsdh_remove(&func->dev);
136         }
137 }
138
139 /* devices we support, null terminated */
140 static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
141         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
142         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
143         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
144         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
145         { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
146         { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE)            },
147         { /* end: all zeroes */                         },
148 };
149
150 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
151
152 static struct sdio_driver bcmsdh_sdmmc_driver = {
153         .probe          = bcmsdh_sdmmc_probe,
154         .remove         = bcmsdh_sdmmc_remove,
155         .name           = "bcmsdh_sdmmc",
156         .id_table       = bcmsdh_sdmmc_ids,
157         };
158
159 struct sdos_info {
160         sdioh_info_t *sd;
161         spinlock_t lock;
162 };
163
164
165 int
166 sdioh_sdmmc_osinit(sdioh_info_t *sd)
167 {
168         struct sdos_info *sdos;
169
170         sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
171         sd->sdos_info = (void*)sdos;
172         if (sdos == NULL)
173                 return BCME_NOMEM;
174
175         sdos->sd = sd;
176         spin_lock_init(&sdos->lock);
177         return BCME_OK;
178 }
179
180 void
181 sdioh_sdmmc_osfree(sdioh_info_t *sd)
182 {
183         struct sdos_info *sdos;
184         ASSERT(sd && sd->sdos_info);
185
186         sdos = (struct sdos_info *)sd->sdos_info;
187         MFREE(sd->osh, sdos, sizeof(struct sdos_info));
188 }
189
190 /* Interrupt enable/disable */
191 SDIOH_API_RC
192 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
193 {
194         ulong flags;
195         struct sdos_info *sdos;
196
197         sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
198
199         sdos = (struct sdos_info *)sd->sdos_info;
200         ASSERT(sdos);
201
202 #if !defined(OOB_INTR_ONLY)
203         if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
204                 sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
205                 return SDIOH_API_RC_FAIL;
206         }
207 #endif /* !defined(OOB_INTR_ONLY) */
208
209         /* Ensure atomicity for enable/disable calls */
210         spin_lock_irqsave(&sdos->lock, flags);
211
212         sd->client_intr_enabled = enable;
213         if (enable) {
214                 sdioh_sdmmc_devintr_on(sd);
215         } else {
216                 sdioh_sdmmc_devintr_off(sd);
217         }
218
219         spin_unlock_irqrestore(&sdos->lock, flags);
220
221         return SDIOH_API_RC_SUCCESS;
222 }
223
224
225 #ifdef BCMSDH_MODULE
226 static int __init
227 bcmsdh_module_init(void)
228 {
229         int error = 0;
230         sdio_function_init();
231         return error;
232 }
233
234 static void __exit
235 bcmsdh_module_cleanup(void)
236 {
237         sdio_function_cleanup();
238 }
239
240 module_init(bcmsdh_module_init);
241 module_exit(bcmsdh_module_cleanup);
242
243 MODULE_LICENSE("GPL v2");
244 MODULE_DESCRIPTION(DESCRIPTION);
245 MODULE_AUTHOR(AUTHOR);
246
247 #endif /* BCMSDH_MODULE */
248 /*
249  * module init
250 */
251 int sdio_function_init(void)
252 {
253         int error = 0;
254         sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
255
256         gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
257         if (!gInstance)
258                 return -ENOMEM;
259
260         error = sdio_register_driver(&bcmsdh_sdmmc_driver);
261
262
263         return error;
264 }
265
266 /*
267  * module cleanup
268 */
269 extern int bcmsdh_remove(struct device *dev);
270 void sdio_function_cleanup(void)
271 {
272         sd_trace(("%s Enter\n", __FUNCTION__));
273
274
275         sdio_unregister_driver(&bcmsdh_sdmmc_driver);
276
277         if (gInstance)
278                 kfree(gInstance);
279 }