Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
[linux-block.git] / drivers / staging / csr / firmware.c
CommitLineData
635d2b00
GKH
1/*
2 * ---------------------------------------------------------------------------
3 * FILE: firmware.c
4 *
5 * PURPOSE:
6 * Implements the f/w related HIP core lib API.
7 * It is part of the porting exercise in Linux.
8 *
9 * Also, it contains example code for reading the loader and f/w files
10 * from the userspace and starting the SME in Linux.
11 *
12 * Copyright (C) 2005-2009 by Cambridge Silicon Radio Ltd.
13 *
14 * Refer to LICENSE.txt included with this source code for details on
15 * the license terms.
16 *
17 * ---------------------------------------------------------------------------
18 */
19#include <linux/kmod.h>
20#include <linux/vmalloc.h>
21#include <linux/firmware.h>
22#include <asm/uaccess.h>
23#include "csr_wifi_hip_unifi.h"
24#include "csr_wifi_hip_unifi_udi.h"
25#include "unifiio.h"
26#include "unifi_priv.h"
27
28/*
29 * ---------------------------------------------------------------------------
30 *
31 * F/W download. Part of the HIP core API
32 *
33 * ---------------------------------------------------------------------------
34 */
35
36
37/*
38 * ---------------------------------------------------------------------------
39 * unifi_fw_read_start
40 *
41 * Returns a structure to be passed in unifi_fw_read().
42 * This structure is an OS specific description of the f/w file.
43 * In the linux implementation it is a buffer with the f/w and its' length.
44 * The HIP driver calls this functions to request for the loader or
45 * the firmware file.
46 * The structure pointer can be freed when unifi_fw_read_stop() is called.
47 *
48 * Arguments:
49 * ospriv Pointer to driver context.
50 * is_fw Type of firmware to retrieve
51 * info Versions information. Can be used to determine
52 * the appropriate f/w file to load.
53 *
54 * Returns:
55 * O on success, non-zero otherwise.
56 *
57 * ---------------------------------------------------------------------------
58 */
59void*
163eb0d8 60unifi_fw_read_start(void *ospriv, s8 is_fw, const card_info_t *info)
635d2b00
GKH
61{
62 unifi_priv_t *priv = (unifi_priv_t*)ospriv;
63 CSR_UNUSED(info);
64
635d2b00
GKH
65 if (is_fw == UNIFI_FW_STA) {
66 /* F/w may have been released after a previous successful download. */
67 if (priv->fw_sta.dl_data == NULL) {
68 unifi_trace(priv, UDBG2, "Attempt reload of sta f/w\n");
69 uf_request_firmware_files(priv, UNIFI_FW_STA);
70 }
71 /* Set up callback struct for readfunc() */
72 if (priv->fw_sta.dl_data != NULL) {
635d2b00
GKH
73 return &priv->fw_sta;
74 }
75
76 } else {
77 unifi_error(priv, "downloading firmware... unknown request: %d\n", is_fw);
78 }
79
635d2b00
GKH
80 return NULL;
81} /* unifi_fw_read_start() */
82
83
84
85/*
86 * ---------------------------------------------------------------------------
87 * unifi_fw_read_stop
88 *
89 * Called when the HIP driver has finished using the loader or
90 * the firmware file.
91 * The firmware buffer may be released now.
92 *
93 * Arguments:
94 * ospriv Pointer to driver context.
95 * dlpriv The pointer returned by unifi_fw_read_start()
96 *
97 * ---------------------------------------------------------------------------
98 */
99void
100unifi_fw_read_stop(void *ospriv, void *dlpriv)
101{
102 unifi_priv_t *priv = (unifi_priv_t*)ospriv;
103 struct dlpriv *dl_struct = (struct dlpriv *)dlpriv;
635d2b00
GKH
104
105 if (dl_struct != NULL) {
106 if (dl_struct->dl_data != NULL) {
107 unifi_trace(priv, UDBG2, "Release f/w buffer %p, %d bytes\n",
108 dl_struct->dl_data, dl_struct->dl_len);
109 }
110 uf_release_firmware(priv, dl_struct);
111 }
112
635d2b00
GKH
113} /* unifi_fw_read_stop() */
114
115
116/*
117 * ---------------------------------------------------------------------------
118 * unifi_fw_open_buffer
119 *
120 * Returns a handle for a buffer dynamically allocated by the driver,
121 * e.g. into which a firmware file may have been converted from another format
122 * which is the case with some production test images.
123 *
124 * The handle may then be used by unifi_fw_read() to access the contents of
125 * the buffer.
126 *
127 * Arguments:
128 * ospriv Pointer to driver context.
129 * fwbuf Buffer containing firmware image
130 * len Length of buffer in bytes
131 *
132 * Returns
133 * Handle for buffer, or NULL on error
134 * ---------------------------------------------------------------------------
135 */
136void *
26a6b2e1 137unifi_fw_open_buffer(void *ospriv, void *fwbuf, u32 len)
635d2b00
GKH
138{
139 unifi_priv_t *priv = (unifi_priv_t*)ospriv;
635d2b00
GKH
140
141 if (fwbuf == NULL) {
635d2b00
GKH
142 return NULL;
143 }
144 priv->fw_conv.dl_data = fwbuf;
145 priv->fw_conv.dl_len = len;
146 priv->fw_conv.fw_desc = NULL; /* No OS f/w resource is associated */
147
635d2b00
GKH
148 return &priv->fw_conv;
149}
150
151/*
152 * ---------------------------------------------------------------------------
153 * unifi_fw_close_buffer
154 *
155 * Releases any handle for a buffer dynamically allocated by the driver,
156 * e.g. into which a firmware file may have been converted from another format
157 * which is the case with some production test images.
158 *
159 *
160 * Arguments:
161 * ospriv Pointer to driver context.
162 * fwbuf Buffer containing firmware image
163 *
164 * Returns
165 * Handle for buffer, or NULL on error
166 * ---------------------------------------------------------------------------
167 */
168void unifi_fw_close_buffer(void *ospriv, void *fwbuf)
169{
170}
171
172/*
173 * ---------------------------------------------------------------------------
174 * unifi_fw_read
175 *
176 * The HIP driver calls this function to ask for a part of the loader or
177 * the firmware file.
178 *
179 * Arguments:
180 * ospriv Pointer to driver context.
181 * arg The pointer returned by unifi_fw_read_start().
182 * offset The offset in the file to return from.
183 * buf A buffer to store the requested data.
184 * len The size of the buf and the size of the requested data.
185 *
186 * Returns
187 * The number of bytes read from the firmware image, or -ve on error
188 * ---------------------------------------------------------------------------
189 */
95e326c2 190s32
26a6b2e1 191unifi_fw_read(void *ospriv, void *arg, u32 offset, void *buf, u32 len)
635d2b00
GKH
192{
193 const struct dlpriv *dlpriv = arg;
194
195 if (offset >= dlpriv->dl_len) {
196 /* at end of file */
197 return 0;
198 }
199
200 if ((offset + len) > dlpriv->dl_len) {
201 /* attempt to read past end of file */
202 return -1;
203 }
204
205 memcpy(buf, dlpriv->dl_data+offset, len);
206
207 return len;
208
209} /* unifi_fw_read() */
210
211
212
213
635d2b00
GKH
214#define UNIFIHELPER_INIT_MODE_SMEUSER 2
215#define UNIFIHELPER_INIT_MODE_NATIVE 1
216
217/*
218 * ---------------------------------------------------------------------------
219 * uf_run_unifihelper
220 *
221 * Ask userspace to send us firmware for download by running
222 * '/usr/sbin/unififw'.
223 * The same script starts the SME userspace application.
224 * Derived from net_run_sbin_hotplug().
225 *
226 * Arguments:
227 * priv Pointer to OS private struct.
228 *
229 * Returns:
230 * None.
231 * ---------------------------------------------------------------------------
232 */
233int
234uf_run_unifihelper(unifi_priv_t *priv)
235{
635d2b00
GKH
236#ifdef ANDROID_BUILD
237 char *prog = "/system/bin/unififw";
238#else
239 char *prog = "/usr/sbin/unififw";
240#endif /* ANDROID_BUILD */
241
242 char *argv[6], *envp[4];
243 char inst_str[8];
244 char init_mode[8];
245 int i, r;
246
247#if (defined CSR_SME_USERSPACE) && (!defined CSR_SUPPORT_WEXT)
248 unifi_trace(priv, UDBG1, "SME userspace build: run unifi_helper manually\n");
249 return 0;
250#endif
251
252 unifi_trace(priv, UDBG1, "starting %s\n", prog);
253
254 snprintf(inst_str, 8, "%d", priv->instance);
255#if (defined CSR_SME_USERSPACE)
256 snprintf(init_mode, 8, "%d", UNIFIHELPER_INIT_MODE_SMEUSER);
257#else
258 snprintf(init_mode, 8, "%d", UNIFIHELPER_INIT_MODE_NATIVE);
259#endif /* CSR_SME_USERSPACE */
260
261 i = 0;
262 argv[i++] = prog;
263 argv[i++] = inst_str;
264 argv[i++] = init_mode;
265 argv[i++] = 0;
266 argv[i] = 0;
267 /* Don't add more args without making argv bigger */
268
269 /* minimal command environment */
270 i = 0;
271 envp[i++] = "HOME=/";
272 envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
273 envp[i] = 0;
274 /* Don't add more without making envp bigger */
275
276 unifi_trace(priv, UDBG2, "running %s %s %s\n", argv[0], argv[1], argv[2]);
277
2b5d20db 278 r = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
635d2b00
GKH
279
280 return r;
635d2b00
GKH
281} /* uf_run_unifihelper() */
282
95edd09e 283#ifdef CSR_WIFI_SPLIT_PATCH
5379b13d 284static u8 is_ap_mode(unifi_priv_t *priv)
95edd09e
GKH
285{
286 if (priv == NULL || priv->interfacePriv[0] == NULL)
287 {
288 return FALSE;
289 }
635d2b00 290
95edd09e
GKH
291 /* Test for mode requiring AP patch */
292 return(CSR_WIFI_HIP_IS_AP_FW(priv->interfacePriv[0]->interfaceMode));
293}
294#endif
635d2b00
GKH
295
296/*
297 * ---------------------------------------------------------------------------
298 * uf_request_firmware_files
299 *
300 * Get the firmware files from userspace.
301 *
302 * Arguments:
303 * priv Pointer to OS private struct.
304 * is_fw type of firmware to load (UNIFI_FW_STA/LOADER)
305 *
306 * Returns:
307 * None.
308 * ---------------------------------------------------------------------------
309 */
310int uf_request_firmware_files(unifi_priv_t *priv, int is_fw)
311{
312 /* uses the default method to get the firmware */
313 const struct firmware *fw_entry;
314 int postfix;
315#define UNIFI_MAX_FW_PATH_LEN 32
316 char fw_name[UNIFI_MAX_FW_PATH_LEN];
317 int r;
318
319#if (defined CSR_SUPPORT_SME) && (defined CSR_SUPPORT_WEXT)
320 if (priv->mib_data.length) {
321 vfree(priv->mib_data.data);
322 priv->mib_data.data = NULL;
323 priv->mib_data.length = 0;
324 }
325#endif /* CSR_SUPPORT_SME && CSR_SUPPORT_WEXT*/
326
327 postfix = priv->instance;
328
329 if (is_fw == UNIFI_FW_STA) {
330 /* Free kernel buffer and reload */
331 uf_release_firmware(priv, &priv->fw_sta);
95edd09e 332#ifdef CSR_WIFI_SPLIT_PATCH
635d2b00 333 scnprintf(fw_name, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s",
95edd09e
GKH
334 postfix, (is_ap_mode(priv) ? "ap.xbv" : "staonly.xbv") );
335#else
336 scnprintf(fw_name, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s",
337 postfix, "sta.xbv" );
338#endif
635d2b00
GKH
339 r = request_firmware(&fw_entry, fw_name, priv->unifi_device);
340 if (r == 0) {
341 priv->fw_sta.dl_data = fw_entry->data;
342 priv->fw_sta.dl_len = fw_entry->size;
343 priv->fw_sta.fw_desc = (void *)fw_entry;
344 } else {
345 unifi_trace(priv, UDBG2, "Firmware file not available\n");
346 }
347 }
348
349 return 0;
350
351} /* uf_request_firmware_files() */
352
353/*
354 * ---------------------------------------------------------------------------
355 * uf_release_firmware_files
356 *
357 * Release all buffers used to store firmware files
358 *
359 * Arguments:
360 * priv Pointer to OS private struct.
361 *
362 * Returns:
363 * None.
364 * ---------------------------------------------------------------------------
365 */
366int uf_release_firmware_files(unifi_priv_t *priv)
367{
368 uf_release_firmware(priv, &priv->fw_sta);
369
370 return 0;
371}
372
373/*
374 * ---------------------------------------------------------------------------
375 * uf_release_firmware
376 *
377 * Release specific buffer used to store firmware
378 *
379 * Arguments:
380 * priv Pointer to OS private struct.
381 * to_free Pointer to specific buffer to release
382 *
383 * Returns:
384 * None.
385 * ---------------------------------------------------------------------------
386 */
387int uf_release_firmware(unifi_priv_t *priv, struct dlpriv *to_free)
388{
389 if (to_free != NULL) {
08fb73c1 390 release_firmware((const struct firmware *)to_free->fw_desc);
635d2b00
GKH
391 to_free->fw_desc = NULL;
392 to_free->dl_data = NULL;
393 to_free->dl_len = 0;
394 }
395 return 0;
396}