wl1271: Fix MAC address handling
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>
Thu, 18 Mar 2010 10:26:39 +0000 (12:26 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 23 Mar 2010 20:50:23 +0000 (16:50 -0400)
This patch fixes MAC address handling. To achieve this, firmware booting had
to be delayed from the previous op_start to op_add_interface, which is the point
when the driver gets to know the configured MAC address. As the wl1271 only
supports one virtual interface, this even seems quite logical.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/wl12xx/wl1271_boot.c
drivers/net/wireless/wl12xx/wl1271_main.c

index f88d52e87e82446c5233fbe47c7b1487f329afb7..41c6affbee29a75657e3534420b02c9ffeda3245 100644 (file)
@@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
        nvs_len = sizeof(wl->nvs->nvs);
        nvs_ptr = (u8 *)wl->nvs->nvs;
 
+       /* update current MAC address to NVS */
+       nvs_ptr[11] = wl->mac_addr[0];
+       nvs_ptr[10] = wl->mac_addr[1];
+       nvs_ptr[6] = wl->mac_addr[2];
+       nvs_ptr[5] = wl->mac_addr[3];
+       nvs_ptr[4] = wl->mac_addr[4];
+       nvs_ptr[3] = wl->mac_addr[5];
+
        /*
         * Layout before the actual NVS tables:
         * 1 byte : burst length.
index 037a4f4036fdc8050d138682dffe851ec82caff8..d8cb51410b2fe1b16c48df2b267977da22b9618d 100644 (file)
@@ -570,40 +570,6 @@ out:
        return ret;
 }
 
-static int wl1271_update_mac_addr(struct wl1271 *wl)
-{
-       int ret = 0;
-       u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
-
-       /* get mac address from the NVS */
-       wl->mac_addr[0] = nvs_ptr[11];
-       wl->mac_addr[1] = nvs_ptr[10];
-       wl->mac_addr[2] = nvs_ptr[6];
-       wl->mac_addr[3] = nvs_ptr[5];
-       wl->mac_addr[4] = nvs_ptr[4];
-       wl->mac_addr[5] = nvs_ptr[3];
-
-       /* FIXME: if it is a zero-address, we should bail out. Now, instead,
-          we randomize an address */
-       if (is_zero_ether_addr(wl->mac_addr)) {
-               static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
-               memcpy(wl->mac_addr, nokia_oui, 3);
-               get_random_bytes(wl->mac_addr + 3, 3);
-
-               /* update this address to the NVS */
-               nvs_ptr[11] = wl->mac_addr[0];
-               nvs_ptr[10] = wl->mac_addr[1];
-               nvs_ptr[6] = wl->mac_addr[2];
-               nvs_ptr[5] = wl->mac_addr[3];
-               nvs_ptr[4] = wl->mac_addr[4];
-               nvs_ptr[3] = wl->mac_addr[5];
-       }
-
-       SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
-
-       return ret;
-}
-
 static int wl1271_fetch_nvs(struct wl1271 *wl)
 {
        const struct firmware *fw;
@@ -633,8 +599,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
 
        memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file));
 
-       ret = wl1271_update_mac_addr(wl);
-
 out:
        release_firmware(fw);
 
@@ -951,14 +915,59 @@ static struct notifier_block wl1271_dev_notifier = {
 
 
 static int wl1271_op_start(struct ieee80211_hw *hw)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 start");
+
+       /*
+        * We have to delay the booting of the hardware because
+        * we need to know the local MAC address before downloading and
+        * initializing the firmware. The MAC address cannot be changed
+        * after boot, and without the proper MAC address, the firmware
+        * will not function properly.
+        *
+        * The MAC address is first known when the corresponding interface
+        * is added. That is where we will initialize the hardware.
+        */
+
+       return 0;
+}
+
+static void wl1271_op_stop(struct ieee80211_hw *hw)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+}
+
+static int wl1271_op_add_interface(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
        int retries = WL1271_BOOT_RETRIES;
        int ret = 0;
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 start");
+       wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
+                    vif->type, vif->addr);
 
        mutex_lock(&wl->mutex);
+       if (wl->vif) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       wl->vif = vif;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               wl->bss_type = BSS_TYPE_STA_BSS;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               wl->bss_type = BSS_TYPE_IBSS;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
        if (wl->state != WL1271_STATE_OFF) {
                wl1271_error("cannot start because not in off state: %d",
@@ -1014,20 +1023,20 @@ out:
        return ret;
 }
 
-static void wl1271_op_stop(struct ieee80211_hw *hw)
+static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
        int i;
 
-       wl1271_info("down");
+       mutex_lock(&wl->mutex);
+       wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+       wl1271_info("down");
 
        unregister_inetaddr_notifier(&wl1271_dev_notifier);
        list_del(&wl->list);
 
-       mutex_lock(&wl->mutex);
-
        WARN_ON(wl->state != WL1271_STATE_ON);
 
        if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
@@ -1070,6 +1079,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        wl->rate_set = CONF_TX_RATE_MASK_BASIC;
        wl->sta_rate_set = 0;
        wl->flags = 0;
+       wl->vif = NULL;
 
        for (i = 0; i < NUM_TX_QUEUES; i++)
                wl->tx_blocks_freed[i] = 0;
@@ -1078,53 +1088,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        mutex_unlock(&wl->mutex);
 }
 
-static int wl1271_op_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif)
-{
-       struct wl1271 *wl = hw->priv;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-                    vif->type, vif->addr);
-
-       mutex_lock(&wl->mutex);
-       if (wl->vif) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       wl->vif = vif;
-
-       switch (vif->type) {
-       case NL80211_IFTYPE_STATION:
-               wl->bss_type = BSS_TYPE_STA_BSS;
-               break;
-       case NL80211_IFTYPE_ADHOC:
-               wl->bss_type = BSS_TYPE_IBSS;
-               break;
-       default:
-               ret = -EOPNOTSUPP;
-               goto out;
-       }
-
-       /* FIXME: what if conf->mac_addr changes? */
-
-out:
-       mutex_unlock(&wl->mutex);
-       return ret;
-}
-
-static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif)
-{
-       struct wl1271 *wl = hw->priv;
-
-       mutex_lock(&wl->mutex);
-       wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
-       wl->vif = NULL;
-       mutex_unlock(&wl->mutex);
-}
-
 #if 0
 static int wl1271_op_config_interface(struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif,
@@ -2114,6 +2077,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
        int i, ret;
+       static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
 
        hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
        if (!hw) {
@@ -2155,6 +2119,13 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->state = WL1271_STATE_OFF;
        mutex_init(&wl->mutex);
 
+       /*
+        * FIXME: we should use a zero MAC address here, but for now we
+        * generate a random Nokia address.
+        */
+       memcpy(wl->mac_addr, nokia_oui, 3);
+       get_random_bytes(wl->mac_addr + 3, 3);
+
        /* Apply default driver configuration. */
        wl1271_conf_init(wl);