wl12xx: 1281/1283 support - Loading FW & NVS
authorShahar Levi <shahar_levi@ti.com>
Sun, 6 Mar 2011 14:32:10 +0000 (16:32 +0200)
committerLuciano Coelho <coelho@ti.com>
Tue, 19 Apr 2011 13:48:10 +0000 (16:48 +0300)
Take care of FW & NVS with the auto-detection between wl127x and
wl128x.

[Moved some common code outside if statements and added notes about
NVS structure assumptions; Fixed a bug when checking the nvs size: if
the size was incorrect, the local nvs variable was set to NULL, it
should be wl->nvs instead. -- Luca]

[Merged with potential buffer overflow fix -- Luca]

Signed-off-by: Shahar Levi <shahar_levi@ti.com>
Reviewed-by: Luciano Coelho <coelho@ti.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/boot.c
drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/ini.h
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/sdio_test.c
drivers/net/wireless/wl12xx/testmode.c
drivers/net/wireless/wl12xx/wl12xx.h

index 69fe8703be423f400ae02609d4ab5031eeed8a2b..38f3e8ba2628c8d54f32c7dcfdc18ed8f0e38eca 100644 (file)
@@ -243,33 +243,57 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
        if (wl->nvs == NULL)
                return -ENODEV;
 
-       /*
-        * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band
-        * configurations) can be removed when those NVS files stop floating
-        * around.
-        */
-       if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
-           wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
-               /* for now 11a is unsupported in AP mode */
-               if (wl->bss_type != BSS_TYPE_AP_BSS &&
-                   wl->nvs->general_params.dual_mode_select)
-                       wl->enable_11a = true;
-       }
+       if (wl->chip.id == CHIP_ID_1283_PG20) {
+               struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
+
+               if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) {
+                       if (nvs->general_params.dual_mode_select)
+                               wl->enable_11a = true;
+               } else {
+                       wl1271_error("nvs size is not as expected: %zu != %zu",
+                                    wl->nvs_len,
+                                    sizeof(struct wl128x_nvs_file));
+                       kfree(wl->nvs);
+                       wl->nvs = NULL;
+                       wl->nvs_len = 0;
+                       return -EILSEQ;
+               }
 
-       if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
-           (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
-            wl->enable_11a)) {
-               wl1271_error("nvs size is not as expected: %zu != %zu",
-                            wl->nvs_len, sizeof(struct wl1271_nvs_file));
-               kfree(wl->nvs);
-               wl->nvs = NULL;
-               wl->nvs_len = 0;
-               return -EILSEQ;
-       }
+               /* only the first part of the NVS needs to be uploaded */
+               nvs_len = sizeof(nvs->nvs);
+               nvs_ptr = (u8 *)nvs->nvs;
 
-       /* only the first part of the NVS needs to be uploaded */
-       nvs_len = sizeof(wl->nvs->nvs);
-       nvs_ptr = (u8 *)wl->nvs->nvs;
+       } else {
+               struct wl1271_nvs_file *nvs =
+                       (struct wl1271_nvs_file *)wl->nvs;
+               /*
+                * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz
+                * band configurations) can be removed when those NVS files stop
+                * floating around.
+                */
+               if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
+                   wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
+                       /* for now 11a is unsupported in AP mode */
+                       if (wl->bss_type != BSS_TYPE_AP_BSS &&
+                           nvs->general_params.dual_mode_select)
+                               wl->enable_11a = true;
+               }
+
+               if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
+                   (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
+                    wl->enable_11a)) {
+                       wl1271_error("nvs size is not as expected: %zu != %zu",
+                               wl->nvs_len, sizeof(struct wl1271_nvs_file));
+                       kfree(wl->nvs);
+                       wl->nvs = NULL;
+                       wl->nvs_len = 0;
+                       return -EILSEQ;
+               }
+
+               /* only the first part of the NVS needs to be uploaded */
+               nvs_len = sizeof(nvs->nvs);
+               nvs_ptr = (u8 *) nvs->nvs;
+       }
 
        /* update current MAC address to NVS */
        nvs_ptr[11] = wl->mac_addr[0];
@@ -319,10 +343,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
        /*
         * We've reached the first zero length, the first NVS table
         * is located at an aligned offset which is at least 7 bytes further.
+        * NOTE: The wl->nvs->nvs element must be first, in order to
+        * simplify the casting, we assume it is at the beginning of
+        * the wl->nvs structure.
         */
-       nvs_ptr = (u8 *)wl->nvs->nvs +
-                       ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4);
-       nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs;
+       nvs_ptr = (u8 *)wl->nvs +
+                       ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);
+       nvs_len -= nvs_ptr - (u8 *)wl->nvs;
 
        /* Now we must set the partition correctly */
        wl1271_set_partition(wl, &part_table[PART_WORK]);
index 37eb9f3669425976bc29eec39dec415e8b1100f5..2468044285172ad4905b6102ac729bd2119a3873 100644 (file)
@@ -187,8 +187,9 @@ out:
 
 int wl1271_cmd_radio_parms(struct wl1271 *wl)
 {
+       struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs;
        struct wl1271_radio_parms_cmd *radio_parms;
-       struct wl1271_ini_general_params *gp = &wl->nvs->general_params;
+       struct wl1271_ini_general_params *gp = &nvs->general_params;
        int ret;
 
        if (!wl->nvs)
@@ -201,18 +202,18 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
        radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
 
        /* 2.4GHz parameters */
-       memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2,
+       memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2,
               sizeof(struct wl1271_ini_band_params_2));
        memcpy(&radio_parms->dyn_params_2,
-              &wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
+              &nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
               sizeof(struct wl1271_ini_fem_params_2));
 
        /* 5GHz parameters */
        memcpy(&radio_parms->static_params_5,
-              &wl->nvs->stat_radio_params_5,
+              &nvs->stat_radio_params_5,
               sizeof(struct wl1271_ini_band_params_5));
        memcpy(&radio_parms->dyn_params_5,
-              &wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
+              &nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
               sizeof(struct wl1271_ini_fem_params_5));
 
        wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
index 30efcd6643b1a10bc0f8a9f6db6fc94b64534610..1420c842b8f1585ac0146071fa88cb63d263cf09 100644 (file)
@@ -174,7 +174,7 @@ struct wl128x_ini_fem_params_5 {
 #define WL1271_INI_LEGACY_NVS_FILE_SIZE              800
 
 struct wl1271_nvs_file {
-       /* NVS section */
+       /* NVS section - must be first! */
        u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
 
        /* INI section */
@@ -195,7 +195,7 @@ struct wl1271_nvs_file {
 } __packed;
 
 struct wl128x_nvs_file {
-       /* NVS section */
+       /* NVS section - must be first! */
        u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
 
        /* INI section */
index e1fd005dd04833980d39fb5d8c0010153b31da7d..fe0cf47a656cdf4b3008047c5a00a9c867093df2 100644 (file)
@@ -804,7 +804,10 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
                break;
        case BSS_TYPE_IBSS:
        case BSS_TYPE_STA_BSS:
-               fw_name = WL1271_FW_NAME;
+               if (wl->chip.id == CHIP_ID_1283_PG20)
+                       fw_name = WL128X_FW_NAME;
+               else
+                       fw_name = WL1271_FW_NAME;
                break;
        default:
                wl1271_error("no compatible firmware for bss_type %d",
@@ -860,7 +863,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
                return ret;
        }
 
-       wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
+       wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
 
        if (!wl->nvs) {
                wl1271_error("could not allocate memory for the nvs file");
@@ -3289,7 +3292,11 @@ int wl1271_register_hw(struct wl1271 *wl)
 
        ret = wl1271_fetch_nvs(wl);
        if (ret == 0) {
-               u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
+               /* NOTE: The wl->nvs->nvs element must be first, in
+                * order to simplify the casting, we assume it is at
+                * the beginning of the wl->nvs structure.
+                */
+               u8 *nvs_ptr = (u8 *)wl->nvs;
 
                wl->mac_addr[0] = nvs_ptr[11];
                wl->mac_addr[1] = nvs_ptr[10];
index 01adf1b1003bd900d07168d50327230014e2203b..e6e2ad63a1c1f02f8b053f46f1a705e81c9683e1 100644 (file)
@@ -189,7 +189,12 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
        const struct firmware *fw;
        int ret;
 
-       ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
+       if (wl->chip.id == CHIP_ID_1283_PG20)
+               ret = request_firmware(&fw, WL128X_FW_NAME,
+                                      wl1271_wl_to_dev(wl));
+       else
+               ret = request_firmware(&fw, WL1271_FW_NAME,
+                                      wl1271_wl_to_dev(wl));
 
        if (ret < 0) {
                wl1271_error("could not get firmware: %d", ret);
@@ -234,7 +239,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
                return ret;
        }
 
-       wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
+       wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
 
        if (!wl->nvs) {
                wl1271_error("could not allocate memory for the nvs file");
index 6ec06a4a4c6dfdbe220f39bc4f910445901fbb22..da351d7cd1f29ff3772f53ac27c2faad561e560d 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "wl12xx.h"
 #include "acx.h"
+#include "reg.h"
 
 #define WL1271_TM_MAX_DATA_LENGTH 1024
 
@@ -204,7 +205,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[])
 
        kfree(wl->nvs);
 
-       if (len != sizeof(struct wl1271_nvs_file))
+       if ((wl->chip.id == CHIP_ID_1283_PG20) &&
+           (len != sizeof(struct wl128x_nvs_file)))
+               return -EINVAL;
+       else if (len != sizeof(struct wl1271_nvs_file))
                return -EINVAL;
 
        wl->nvs = kzalloc(len, GFP_KERNEL);
index 959b338d0af43b77f4ff910aa18042c3347f92cf..e59f5392e909def6312b75ff3dee2797c0bda823 100644 (file)
@@ -378,7 +378,7 @@ struct wl1271 {
        u8 *fw;
        size_t fw_len;
        u8 fw_bss_type;
-       struct wl1271_nvs_file *nvs;
+       void *nvs;
        size_t nvs_len;
 
        s8 hw_pg_ver;