diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index b13eed129a92..e29343ed6ea2 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -458,6 +458,97 @@ static void wl12xx_free_link(struct wl1271 *wl, u8 *hlid) *hlid = WL12XX_INVALID_LINK_ID; } +int wl12xx_cmd_role_start_dev(struct wl1271 *wl) +{ + struct wl12xx_cmd_role_start *cmd; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wl->dev_role_id); + + cmd->role_id = wl->dev_role_id; + if (wl->band == IEEE80211_BAND_5GHZ) + cmd->band = WL12XX_BAND_5GHZ; + cmd->channel = wl->channel; + + if (wl->dev_hlid == WL12XX_INVALID_LINK_ID) { + ret = wl12xx_allocate_link(wl, &wl->dev_hlid); + if (ret) + goto out_free; + } + cmd->device.hlid = wl->dev_hlid; + cmd->device.session = wl->session_counter; + + wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", + cmd->role_id, cmd->device.hlid, cmd->device.session); + + ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd role enable"); + goto err_hlid; + } + + goto out_free; + +err_hlid: + /* clear links on error */ + __clear_bit(wl->dev_hlid, wl->links_map); + wl->dev_hlid = WL12XX_INVALID_LINK_ID; + + +out_free: + kfree(cmd); + +out: + return ret; +} + +int wl12xx_cmd_role_stop_dev(struct wl1271 *wl) +{ + struct wl12xx_cmd_role_stop *cmd; + int ret; + + if (WARN_ON(wl->dev_hlid == WL12XX_INVALID_LINK_ID)) + return -EINVAL; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + wl1271_debug(DEBUG_CMD, "cmd role stop dev"); + + cmd->role_id = wl->dev_role_id; + cmd->disc_type = DISCONNECT_IMMEDIATE; + cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED); + + ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to initiate cmd role stop"); + goto out_free; + } + + ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID); + if (ret < 0) { + wl1271_error("cmd role stop dev event completion error"); + goto out_free; + } + + wl12xx_free_link(wl, &wl->dev_hlid); + +out_free: + kfree(cmd); + +out: + return ret; +} + int wl12xx_cmd_role_start_sta(struct wl1271 *wl) { struct wl12xx_cmd_role_start *cmd; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3e77f59e3397..7b0b7c34ef76 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1866,6 +1866,20 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, if (ret < 0) goto power_off; + if (wl->bss_type == BSS_TYPE_STA_BSS) { + /* + * The device role is a special role used for + * rx and tx frames prior to association (as + * the STA role can get packets only from + * its associated bssid) + */ + ret = wl12xx_cmd_role_enable(wl, + WL1271_ROLE_DEVICE, + &wl->dev_role_id); + if (ret < 0) + goto irq_disable; + } + ret = wl12xx_cmd_role_enable(wl, role_type, &wl->role_id); if (ret < 0) goto irq_disable; @@ -1965,6 +1979,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, if (ret < 0) goto deinit; + if (wl->bss_type == BSS_TYPE_STA_BSS) { + ret = wl12xx_cmd_role_disable(wl, &wl->dev_role_id); + if (ret < 0) + goto deinit; + } + ret = wl12xx_cmd_role_disable(wl, &wl->role_id); if (ret < 0) goto deinit; @@ -1973,6 +1993,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, } deinit: wl->sta_hlid = WL12XX_INVALID_LINK_ID; + wl->dev_hlid = WL12XX_INVALID_LINK_ID; /* * this must be before the cancel_work calls below, so that the work @@ -2023,6 +2044,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl->ap_ps_map = 0; wl->sched_scanning = false; wl->role_id = WL12XX_INVALID_ROLE_ID; + wl->dev_role_id = WL12XX_INVALID_ROLE_ID; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); @@ -4365,7 +4387,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->tx_security_last_seq_lsb = 0; wl->role_id = WL12XX_INVALID_ROLE_ID; wl->sta_hlid = WL12XX_INVALID_LINK_ID; - + wl->dev_role_id = WL12XX_INVALID_ROLE_ID; + wl->dev_hlid = WL12XX_INVALID_LINK_ID; setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer, (unsigned long) wl); wl->fwlog_size = 0; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 3d43875163e1..ab46664969ec 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -394,6 +394,7 @@ struct wl1271 { u8 ssid_len; int channel; u8 role_id; + u8 dev_role_id; u8 sta_hlid; u8 dev_hlid;