2 * ---------------------------------------------------------------------------
6 * Implements the f/w related HIP core lib API.
7 * It is part of the porting exercise in Linux.
9 * Also, it contains example code for reading the loader and f/w files
10 * from the userspace and starting the SME in Linux.
12 * Copyright (C) 2005-2009 by Cambridge Silicon Radio Ltd.
14 * Refer to LICENSE.txt included with this source code for details on
17 * ---------------------------------------------------------------------------
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"
26 #include "unifi_priv.h"
29 * ---------------------------------------------------------------------------
31 * F/W download. Part of the HIP core API
33 * ---------------------------------------------------------------------------
38 * ---------------------------------------------------------------------------
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
46 * The structure pointer can be freed when unifi_fw_read_stop() is called.
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.
55 * O on success, non-zero otherwise.
57 * ---------------------------------------------------------------------------
60 unifi_fw_read_start(void *ospriv
, s8 is_fw
, const card_info_t
*info
)
62 unifi_priv_t
*priv
= (unifi_priv_t
*)ospriv
;
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
);
71 /* Set up callback struct for readfunc() */
72 if (priv
->fw_sta
.dl_data
!= NULL
) {
78 unifi_error(priv
, "downloading firmware... unknown request: %d\n", is_fw
);
83 } /* unifi_fw_read_start() */
88 * ---------------------------------------------------------------------------
91 * Called when the HIP driver has finished using the loader or
93 * The firmware buffer may be released now.
96 * ospriv Pointer to driver context.
97 * dlpriv The pointer returned by unifi_fw_read_start()
99 * ---------------------------------------------------------------------------
102 unifi_fw_read_stop(void *ospriv
, void *dlpriv
)
104 unifi_priv_t
*priv
= (unifi_priv_t
*)ospriv
;
105 struct dlpriv
*dl_struct
= (struct dlpriv
*)dlpriv
;
107 if (dl_struct
!= NULL
) {
108 if (dl_struct
->dl_data
!= NULL
) {
109 unifi_trace(priv
, UDBG2
, "Release f/w buffer %p, %d bytes\n",
110 dl_struct
->dl_data
, dl_struct
->dl_len
);
112 uf_release_firmware(priv
, dl_struct
);
116 } /* unifi_fw_read_stop() */
120 * ---------------------------------------------------------------------------
121 * unifi_fw_open_buffer
123 * Returns a handle for a buffer dynamically allocated by the driver,
124 * e.g. into which a firmware file may have been converted from another format
125 * which is the case with some production test images.
127 * The handle may then be used by unifi_fw_read() to access the contents of
131 * ospriv Pointer to driver context.
132 * fwbuf Buffer containing firmware image
133 * len Length of buffer in bytes
136 * Handle for buffer, or NULL on error
137 * ---------------------------------------------------------------------------
140 unifi_fw_open_buffer(void *ospriv
, void *fwbuf
, u32 len
)
142 unifi_priv_t
*priv
= (unifi_priv_t
*)ospriv
;
148 priv
->fw_conv
.dl_data
= fwbuf
;
149 priv
->fw_conv
.dl_len
= len
;
150 priv
->fw_conv
.fw_desc
= NULL
; /* No OS f/w resource is associated */
153 return &priv
->fw_conv
;
157 * ---------------------------------------------------------------------------
158 * unifi_fw_close_buffer
160 * Releases any handle for a buffer dynamically allocated by the driver,
161 * e.g. into which a firmware file may have been converted from another format
162 * which is the case with some production test images.
166 * ospriv Pointer to driver context.
167 * fwbuf Buffer containing firmware image
170 * Handle for buffer, or NULL on error
171 * ---------------------------------------------------------------------------
173 void unifi_fw_close_buffer(void *ospriv
, void *fwbuf
)
178 * ---------------------------------------------------------------------------
181 * The HIP driver calls this function to ask for a part of the loader or
185 * ospriv Pointer to driver context.
186 * arg The pointer returned by unifi_fw_read_start().
187 * offset The offset in the file to return from.
188 * buf A buffer to store the requested data.
189 * len The size of the buf and the size of the requested data.
192 * The number of bytes read from the firmware image, or -ve on error
193 * ---------------------------------------------------------------------------
196 unifi_fw_read(void *ospriv
, void *arg
, u32 offset
, void *buf
, u32 len
)
198 const struct dlpriv
*dlpriv
= arg
;
200 if (offset
>= dlpriv
->dl_len
) {
205 if ((offset
+ len
) > dlpriv
->dl_len
) {
206 /* attempt to read past end of file */
210 memcpy(buf
, dlpriv
->dl_data
+offset
, len
);
214 } /* unifi_fw_read() */
219 #define UNIFIHELPER_INIT_MODE_SMEUSER 2
220 #define UNIFIHELPER_INIT_MODE_NATIVE 1
223 * ---------------------------------------------------------------------------
226 * Ask userspace to send us firmware for download by running
227 * '/usr/sbin/unififw'.
228 * The same script starts the SME userspace application.
229 * Derived from net_run_sbin_hotplug().
232 * priv Pointer to OS private struct.
236 * ---------------------------------------------------------------------------
239 uf_run_unifihelper(unifi_priv_t
*priv
)
241 #ifdef CONFIG_HOTPLUG
244 char *prog
= "/system/bin/unififw";
246 char *prog
= "/usr/sbin/unififw";
247 #endif /* ANDROID_BUILD */
249 char *argv
[6], *envp
[4];
254 #if (defined CSR_SME_USERSPACE) && (!defined CSR_SUPPORT_WEXT)
255 unifi_trace(priv
, UDBG1
, "SME userspace build: run unifi_helper manually\n");
259 unifi_trace(priv
, UDBG1
, "starting %s\n", prog
);
261 snprintf(inst_str
, 8, "%d", priv
->instance
);
262 #if (defined CSR_SME_USERSPACE)
263 snprintf(init_mode
, 8, "%d", UNIFIHELPER_INIT_MODE_SMEUSER
);
265 snprintf(init_mode
, 8, "%d", UNIFIHELPER_INIT_MODE_NATIVE
);
266 #endif /* CSR_SME_USERSPACE */
270 argv
[i
++] = inst_str
;
271 argv
[i
++] = init_mode
;
274 /* Don't add more args without making argv bigger */
276 /* minimal command environment */
278 envp
[i
++] = "HOME=/";
279 envp
[i
++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
281 /* Don't add more without making envp bigger */
283 unifi_trace(priv
, UDBG2
, "running %s %s %s\n", argv
[0], argv
[1], argv
[2]);
285 r
= call_usermodehelper(argv
[0], argv
, envp
, UMH_WAIT_EXEC
);
289 unifi_trace(priv
, UDBG1
, "Can't automatically download firmware because kernel does not have HOTPLUG\n");
292 } /* uf_run_unifihelper() */
294 #ifdef CSR_WIFI_SPLIT_PATCH
295 static u8
is_ap_mode(unifi_priv_t
*priv
)
297 if (priv
== NULL
|| priv
->interfacePriv
[0] == NULL
)
302 /* Test for mode requiring AP patch */
303 return(CSR_WIFI_HIP_IS_AP_FW(priv
->interfacePriv
[0]->interfaceMode
));
308 * ---------------------------------------------------------------------------
309 * uf_request_firmware_files
311 * Get the firmware files from userspace.
314 * priv Pointer to OS private struct.
315 * is_fw type of firmware to load (UNIFI_FW_STA/LOADER)
319 * ---------------------------------------------------------------------------
321 int uf_request_firmware_files(unifi_priv_t
*priv
, int is_fw
)
323 /* uses the default method to get the firmware */
324 const struct firmware
*fw_entry
;
326 #define UNIFI_MAX_FW_PATH_LEN 32
327 char fw_name
[UNIFI_MAX_FW_PATH_LEN
];
330 #if (defined CSR_SUPPORT_SME) && (defined CSR_SUPPORT_WEXT)
331 if (priv
->mib_data
.length
) {
332 vfree(priv
->mib_data
.data
);
333 priv
->mib_data
.data
= NULL
;
334 priv
->mib_data
.length
= 0;
336 #endif /* CSR_SUPPORT_SME && CSR_SUPPORT_WEXT*/
338 postfix
= priv
->instance
;
340 if (is_fw
== UNIFI_FW_STA
) {
341 /* Free kernel buffer and reload */
342 uf_release_firmware(priv
, &priv
->fw_sta
);
343 #ifdef CSR_WIFI_SPLIT_PATCH
344 scnprintf(fw_name
, UNIFI_MAX_FW_PATH_LEN
, "unifi-sdio-%d/%s",
345 postfix
, (is_ap_mode(priv
) ? "ap.xbv" : "staonly.xbv") );
347 scnprintf(fw_name
, UNIFI_MAX_FW_PATH_LEN
, "unifi-sdio-%d/%s",
348 postfix
, "sta.xbv" );
350 r
= request_firmware(&fw_entry
, fw_name
, priv
->unifi_device
);
352 priv
->fw_sta
.dl_data
= fw_entry
->data
;
353 priv
->fw_sta
.dl_len
= fw_entry
->size
;
354 priv
->fw_sta
.fw_desc
= (void *)fw_entry
;
356 unifi_trace(priv
, UDBG2
, "Firmware file not available\n");
362 } /* uf_request_firmware_files() */
365 * ---------------------------------------------------------------------------
366 * uf_release_firmware_files
368 * Release all buffers used to store firmware files
371 * priv Pointer to OS private struct.
375 * ---------------------------------------------------------------------------
377 int uf_release_firmware_files(unifi_priv_t
*priv
)
379 uf_release_firmware(priv
, &priv
->fw_sta
);
385 * ---------------------------------------------------------------------------
386 * uf_release_firmware
388 * Release specific buffer used to store firmware
391 * priv Pointer to OS private struct.
392 * to_free Pointer to specific buffer to release
396 * ---------------------------------------------------------------------------
398 int uf_release_firmware(unifi_priv_t
*priv
, struct dlpriv
*to_free
)
400 if (to_free
!= NULL
) {
401 release_firmware((const struct firmware
*)to_free
->fw_desc
);
402 to_free
->fw_desc
= NULL
;
403 to_free
->dl_data
= NULL
;