Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.9-rc5 396 lines 12 kB view raw
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* 60unifi_fw_read_start(void *ospriv, s8 is_fw, const card_info_t *info) 61{ 62 unifi_priv_t *priv = (unifi_priv_t*)ospriv; 63 CSR_UNUSED(info); 64 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) { 73 return &priv->fw_sta; 74 } 75 76 } else { 77 unifi_error(priv, "downloading firmware... unknown request: %d\n", is_fw); 78 } 79 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; 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 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 * 137unifi_fw_open_buffer(void *ospriv, void *fwbuf, u32 len) 138{ 139 unifi_priv_t *priv = (unifi_priv_t*)ospriv; 140 141 if (fwbuf == NULL) { 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 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 */ 190s32 191unifi_fw_read(void *ospriv, void *arg, u32 offset, void *buf, u32 len) 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 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{ 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 278 r = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); 279 280 return r; 281} /* uf_run_unifihelper() */ 282 283#ifdef CSR_WIFI_SPLIT_PATCH 284static u8 is_ap_mode(unifi_priv_t *priv) 285{ 286 if (priv == NULL || priv->interfacePriv[0] == NULL) 287 { 288 return FALSE; 289 } 290 291 /* Test for mode requiring AP patch */ 292 return(CSR_WIFI_HIP_IS_AP_FW(priv->interfacePriv[0]->interfaceMode)); 293} 294#endif 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); 332#ifdef CSR_WIFI_SPLIT_PATCH 333 scnprintf(fw_name, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s", 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 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) { 390 release_firmware((const struct firmware *)to_free->fw_desc); 391 to_free->fw_desc = NULL; 392 to_free->dl_data = NULL; 393 to_free->dl_len = 0; 394 } 395 return 0; 396}