Commit | Line | Data |
---|---|---|
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 | */ | |
59 | void* | |
163eb0d8 | 60 | unifi_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 | ||
65 | func_enter(); | |
66 | ||
67 | if (is_fw == UNIFI_FW_STA) { | |
68 | /* F/w may have been released after a previous successful download. */ | |
69 | if (priv->fw_sta.dl_data == NULL) { | |
70 | unifi_trace(priv, UDBG2, "Attempt reload of sta f/w\n"); | |
71 | uf_request_firmware_files(priv, UNIFI_FW_STA); | |
72 | } | |
73 | /* Set up callback struct for readfunc() */ | |
74 | if (priv->fw_sta.dl_data != NULL) { | |
75 | func_exit(); | |
76 | return &priv->fw_sta; | |
77 | } | |
78 | ||
79 | } else { | |
80 | unifi_error(priv, "downloading firmware... unknown request: %d\n", is_fw); | |
81 | } | |
82 | ||
83 | func_exit(); | |
84 | return NULL; | |
85 | } /* unifi_fw_read_start() */ | |
86 | ||
87 | ||
88 | ||
89 | /* | |
90 | * --------------------------------------------------------------------------- | |
91 | * unifi_fw_read_stop | |
92 | * | |
93 | * Called when the HIP driver has finished using the loader or | |
94 | * the firmware file. | |
95 | * The firmware buffer may be released now. | |
96 | * | |
97 | * Arguments: | |
98 | * ospriv Pointer to driver context. | |
99 | * dlpriv The pointer returned by unifi_fw_read_start() | |
100 | * | |
101 | * --------------------------------------------------------------------------- | |
102 | */ | |
103 | void | |
104 | unifi_fw_read_stop(void *ospriv, void *dlpriv) | |
105 | { | |
106 | unifi_priv_t *priv = (unifi_priv_t*)ospriv; | |
107 | struct dlpriv *dl_struct = (struct dlpriv *)dlpriv; | |
108 | func_enter(); | |
109 | ||
110 | if (dl_struct != NULL) { | |
111 | if (dl_struct->dl_data != NULL) { | |
112 | unifi_trace(priv, UDBG2, "Release f/w buffer %p, %d bytes\n", | |
113 | dl_struct->dl_data, dl_struct->dl_len); | |
114 | } | |
115 | uf_release_firmware(priv, dl_struct); | |
116 | } | |
117 | ||
118 | func_exit(); | |
119 | } /* unifi_fw_read_stop() */ | |
120 | ||
121 | ||
122 | /* | |
123 | * --------------------------------------------------------------------------- | |
124 | * unifi_fw_open_buffer | |
125 | * | |
126 | * Returns a handle for a buffer dynamically allocated by the driver, | |
127 | * e.g. into which a firmware file may have been converted from another format | |
128 | * which is the case with some production test images. | |
129 | * | |
130 | * The handle may then be used by unifi_fw_read() to access the contents of | |
131 | * the buffer. | |
132 | * | |
133 | * Arguments: | |
134 | * ospriv Pointer to driver context. | |
135 | * fwbuf Buffer containing firmware image | |
136 | * len Length of buffer in bytes | |
137 | * | |
138 | * Returns | |
139 | * Handle for buffer, or NULL on error | |
140 | * --------------------------------------------------------------------------- | |
141 | */ | |
142 | void * | |
26a6b2e1 | 143 | unifi_fw_open_buffer(void *ospriv, void *fwbuf, u32 len) |
635d2b00 GKH |
144 | { |
145 | unifi_priv_t *priv = (unifi_priv_t*)ospriv; | |
146 | func_enter(); | |
147 | ||
148 | if (fwbuf == NULL) { | |
149 | func_exit(); | |
150 | return NULL; | |
151 | } | |
152 | priv->fw_conv.dl_data = fwbuf; | |
153 | priv->fw_conv.dl_len = len; | |
154 | priv->fw_conv.fw_desc = NULL; /* No OS f/w resource is associated */ | |
155 | ||
156 | func_exit(); | |
157 | return &priv->fw_conv; | |
158 | } | |
159 | ||
160 | /* | |
161 | * --------------------------------------------------------------------------- | |
162 | * unifi_fw_close_buffer | |
163 | * | |
164 | * Releases any handle for a buffer dynamically allocated by the driver, | |
165 | * e.g. into which a firmware file may have been converted from another format | |
166 | * which is the case with some production test images. | |
167 | * | |
168 | * | |
169 | * Arguments: | |
170 | * ospriv Pointer to driver context. | |
171 | * fwbuf Buffer containing firmware image | |
172 | * | |
173 | * Returns | |
174 | * Handle for buffer, or NULL on error | |
175 | * --------------------------------------------------------------------------- | |
176 | */ | |
177 | void unifi_fw_close_buffer(void *ospriv, void *fwbuf) | |
178 | { | |
179 | } | |
180 | ||
181 | /* | |
182 | * --------------------------------------------------------------------------- | |
183 | * unifi_fw_read | |
184 | * | |
185 | * The HIP driver calls this function to ask for a part of the loader or | |
186 | * the firmware file. | |
187 | * | |
188 | * Arguments: | |
189 | * ospriv Pointer to driver context. | |
190 | * arg The pointer returned by unifi_fw_read_start(). | |
191 | * offset The offset in the file to return from. | |
192 | * buf A buffer to store the requested data. | |
193 | * len The size of the buf and the size of the requested data. | |
194 | * | |
195 | * Returns | |
196 | * The number of bytes read from the firmware image, or -ve on error | |
197 | * --------------------------------------------------------------------------- | |
198 | */ | |
95e326c2 | 199 | s32 |
26a6b2e1 | 200 | unifi_fw_read(void *ospriv, void *arg, u32 offset, void *buf, u32 len) |
635d2b00 GKH |
201 | { |
202 | const struct dlpriv *dlpriv = arg; | |
203 | ||
204 | if (offset >= dlpriv->dl_len) { | |
205 | /* at end of file */ | |
206 | return 0; | |
207 | } | |
208 | ||
209 | if ((offset + len) > dlpriv->dl_len) { | |
210 | /* attempt to read past end of file */ | |
211 | return -1; | |
212 | } | |
213 | ||
214 | memcpy(buf, dlpriv->dl_data+offset, len); | |
215 | ||
216 | return len; | |
217 | ||
218 | } /* unifi_fw_read() */ | |
219 | ||
220 | ||
221 | ||
222 | ||
635d2b00 GKH |
223 | #define UNIFIHELPER_INIT_MODE_SMEUSER 2 |
224 | #define UNIFIHELPER_INIT_MODE_NATIVE 1 | |
225 | ||
226 | /* | |
227 | * --------------------------------------------------------------------------- | |
228 | * uf_run_unifihelper | |
229 | * | |
230 | * Ask userspace to send us firmware for download by running | |
231 | * '/usr/sbin/unififw'. | |
232 | * The same script starts the SME userspace application. | |
233 | * Derived from net_run_sbin_hotplug(). | |
234 | * | |
235 | * Arguments: | |
236 | * priv Pointer to OS private struct. | |
237 | * | |
238 | * Returns: | |
239 | * None. | |
240 | * --------------------------------------------------------------------------- | |
241 | */ | |
242 | int | |
243 | uf_run_unifihelper(unifi_priv_t *priv) | |
244 | { | |
245 | #ifdef CONFIG_HOTPLUG | |
246 | ||
247 | #ifdef ANDROID_BUILD | |
248 | char *prog = "/system/bin/unififw"; | |
249 | #else | |
250 | char *prog = "/usr/sbin/unififw"; | |
251 | #endif /* ANDROID_BUILD */ | |
252 | ||
253 | char *argv[6], *envp[4]; | |
254 | char inst_str[8]; | |
255 | char init_mode[8]; | |
256 | int i, r; | |
257 | ||
258 | #if (defined CSR_SME_USERSPACE) && (!defined CSR_SUPPORT_WEXT) | |
259 | unifi_trace(priv, UDBG1, "SME userspace build: run unifi_helper manually\n"); | |
260 | return 0; | |
261 | #endif | |
262 | ||
263 | unifi_trace(priv, UDBG1, "starting %s\n", prog); | |
264 | ||
265 | snprintf(inst_str, 8, "%d", priv->instance); | |
266 | #if (defined CSR_SME_USERSPACE) | |
267 | snprintf(init_mode, 8, "%d", UNIFIHELPER_INIT_MODE_SMEUSER); | |
268 | #else | |
269 | snprintf(init_mode, 8, "%d", UNIFIHELPER_INIT_MODE_NATIVE); | |
270 | #endif /* CSR_SME_USERSPACE */ | |
271 | ||
272 | i = 0; | |
273 | argv[i++] = prog; | |
274 | argv[i++] = inst_str; | |
275 | argv[i++] = init_mode; | |
276 | argv[i++] = 0; | |
277 | argv[i] = 0; | |
278 | /* Don't add more args without making argv bigger */ | |
279 | ||
280 | /* minimal command environment */ | |
281 | i = 0; | |
282 | envp[i++] = "HOME=/"; | |
283 | envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
284 | envp[i] = 0; | |
285 | /* Don't add more without making envp bigger */ | |
286 | ||
287 | unifi_trace(priv, UDBG2, "running %s %s %s\n", argv[0], argv[1], argv[2]); | |
288 | ||
289 | r = call_usermodehelper(argv[0], argv, envp, 0); | |
290 | ||
291 | return r; | |
292 | #else | |
293 | unifi_trace(priv, UDBG1, "Can't automatically download firmware because kernel does not have HOTPLUG\n"); | |
294 | return -1; | |
295 | #endif | |
296 | } /* uf_run_unifihelper() */ | |
297 | ||
95edd09e GKH |
298 | #ifdef CSR_WIFI_SPLIT_PATCH |
299 | static CsrBool is_ap_mode(unifi_priv_t *priv) | |
300 | { | |
301 | if (priv == NULL || priv->interfacePriv[0] == NULL) | |
302 | { | |
303 | return FALSE; | |
304 | } | |
635d2b00 | 305 | |
95edd09e GKH |
306 | /* Test for mode requiring AP patch */ |
307 | return(CSR_WIFI_HIP_IS_AP_FW(priv->interfacePriv[0]->interfaceMode)); | |
308 | } | |
309 | #endif | |
635d2b00 GKH |
310 | |
311 | /* | |
312 | * --------------------------------------------------------------------------- | |
313 | * uf_request_firmware_files | |
314 | * | |
315 | * Get the firmware files from userspace. | |
316 | * | |
317 | * Arguments: | |
318 | * priv Pointer to OS private struct. | |
319 | * is_fw type of firmware to load (UNIFI_FW_STA/LOADER) | |
320 | * | |
321 | * Returns: | |
322 | * None. | |
323 | * --------------------------------------------------------------------------- | |
324 | */ | |
325 | int uf_request_firmware_files(unifi_priv_t *priv, int is_fw) | |
326 | { | |
327 | /* uses the default method to get the firmware */ | |
328 | const struct firmware *fw_entry; | |
329 | int postfix; | |
330 | #define UNIFI_MAX_FW_PATH_LEN 32 | |
331 | char fw_name[UNIFI_MAX_FW_PATH_LEN]; | |
332 | int r; | |
333 | ||
334 | #if (defined CSR_SUPPORT_SME) && (defined CSR_SUPPORT_WEXT) | |
335 | if (priv->mib_data.length) { | |
336 | vfree(priv->mib_data.data); | |
337 | priv->mib_data.data = NULL; | |
338 | priv->mib_data.length = 0; | |
339 | } | |
340 | #endif /* CSR_SUPPORT_SME && CSR_SUPPORT_WEXT*/ | |
341 | ||
342 | postfix = priv->instance; | |
343 | ||
344 | if (is_fw == UNIFI_FW_STA) { | |
345 | /* Free kernel buffer and reload */ | |
346 | uf_release_firmware(priv, &priv->fw_sta); | |
95edd09e | 347 | #ifdef CSR_WIFI_SPLIT_PATCH |
635d2b00 | 348 | scnprintf(fw_name, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s", |
95edd09e GKH |
349 | postfix, (is_ap_mode(priv) ? "ap.xbv" : "staonly.xbv") ); |
350 | #else | |
351 | scnprintf(fw_name, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s", | |
352 | postfix, "sta.xbv" ); | |
353 | #endif | |
635d2b00 GKH |
354 | r = request_firmware(&fw_entry, fw_name, priv->unifi_device); |
355 | if (r == 0) { | |
356 | priv->fw_sta.dl_data = fw_entry->data; | |
357 | priv->fw_sta.dl_len = fw_entry->size; | |
358 | priv->fw_sta.fw_desc = (void *)fw_entry; | |
359 | } else { | |
360 | unifi_trace(priv, UDBG2, "Firmware file not available\n"); | |
361 | } | |
362 | } | |
363 | ||
364 | return 0; | |
365 | ||
366 | } /* uf_request_firmware_files() */ | |
367 | ||
368 | /* | |
369 | * --------------------------------------------------------------------------- | |
370 | * uf_release_firmware_files | |
371 | * | |
372 | * Release all buffers used to store firmware files | |
373 | * | |
374 | * Arguments: | |
375 | * priv Pointer to OS private struct. | |
376 | * | |
377 | * Returns: | |
378 | * None. | |
379 | * --------------------------------------------------------------------------- | |
380 | */ | |
381 | int uf_release_firmware_files(unifi_priv_t *priv) | |
382 | { | |
383 | uf_release_firmware(priv, &priv->fw_sta); | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
388 | /* | |
389 | * --------------------------------------------------------------------------- | |
390 | * uf_release_firmware | |
391 | * | |
392 | * Release specific buffer used to store firmware | |
393 | * | |
394 | * Arguments: | |
395 | * priv Pointer to OS private struct. | |
396 | * to_free Pointer to specific buffer to release | |
397 | * | |
398 | * Returns: | |
399 | * None. | |
400 | * --------------------------------------------------------------------------- | |
401 | */ | |
402 | int uf_release_firmware(unifi_priv_t *priv, struct dlpriv *to_free) | |
403 | { | |
404 | if (to_free != NULL) { | |
405 | if (to_free->fw_desc != NULL) { | |
406 | release_firmware((const struct firmware *)to_free->fw_desc); | |
407 | } | |
408 | to_free->fw_desc = NULL; | |
409 | to_free->dl_data = NULL; | |
410 | to_free->dl_len = 0; | |
411 | } | |
412 | return 0; | |
413 | } |