From 0ce77920adcb16d6449de9ca481a553ea6008c6d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 11 Aug 2011 00:32:49 +0300 Subject: [PATCH 001/155] ath6kl: Use cfg80211_inform_bss instead of cfg80211_inform_bss_frame There is no point in generating a bogus Beacon frame for cfg80211_inform_bss_frame when cfg80211_inform_bss can be used instead. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 44 +++++++++------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 14559ffb1453..201398ec4b82 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -723,8 +723,6 @@ static inline bool is_ch_11a(u16 ch) /* struct ath6kl_node_table::nt_nodelock is locked when calling this */ void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni) { - u16 size; - unsigned char *ieeemgmtbuf = NULL; struct ieee80211_mgmt *mgmt; struct ieee80211_channel *channel; struct ieee80211_supported_band *band; @@ -741,37 +739,29 @@ void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni) else band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */ - size = ni->ni_framelen + offsetof(struct ieee80211_mgmt, u); - ieeemgmtbuf = kmalloc(size, GFP_ATOMIC); - if (!ieeemgmtbuf) { - ath6kl_err("ieee mgmt buf alloc error\n"); - return; - } - - /* - * TODO: Update target to include 802.11 mac header while sending - * bss info. Target removes 802.11 mac header while sending the bss - * info to host, cfg80211 needs it, for time being just filling the - * da, sa and bssid fields alone. - */ - mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf; - memset(mgmt->da, 0xff, ETH_ALEN); /*broadcast addr */ - memcpy(mgmt->sa, ni->ni_macaddr, ETH_ALEN); - memcpy(mgmt->bssid, ni->ni_macaddr, ETH_ALEN); - memcpy(ieeemgmtbuf + offsetof(struct ieee80211_mgmt, u), - ni->ni_buf, ni->ni_framelen); - freq = cie->ie_chan; channel = ieee80211_get_channel(wiphy, freq); signal = ni->ni_snr * 100; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: bssid %pM ch %d freq %d size %d\n", __func__, - mgmt->bssid, channel->hw_value, freq, size); - cfg80211_inform_bss_frame(wiphy, channel, mgmt, - size, signal, GFP_ATOMIC); - - kfree(ieeemgmtbuf); + ni->ni_macaddr, channel->hw_value, freq, ni->ni_framelen); + /* + * Both Beacon and Probe Response frames have same payload structure, + * so it is fine to share the parser for both. + */ + if (ni->ni_framelen < 8 + 2 + 2) + return; + mgmt = (struct ieee80211_mgmt *) (ni->ni_buf - + offsetof(struct ieee80211_mgmt, u)); + cfg80211_inform_bss(wiphy, channel, ni->ni_macaddr, + le64_to_cpu(mgmt->u.beacon.timestamp), + le16_to_cpu(mgmt->u.beacon.capab_info), + le16_to_cpu(mgmt->u.beacon.beacon_int), + mgmt->u.beacon.variable, + ni->ni_buf + ni->ni_framelen - + mgmt->u.beacon.variable, + signal, GFP_ATOMIC); } static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, From 31024d99003486c90c793dea58b55f7920f0488b Mon Sep 17 00:00:00 2001 From: Kevin Fang Date: Mon, 11 Jul 2011 17:14:13 +0800 Subject: [PATCH 002/155] ath6kl: Add beginning of AR6004 initialisation support Support isn't complete yet. Signed-off-by: Kevin Fang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/bmi.h | 4 +- drivers/net/wireless/ath/ath6kl/core.h | 7 ++ drivers/net/wireless/ath/ath6kl/init.c | 136 +++++++++++++++++------ drivers/net/wireless/ath/ath6kl/main.c | 20 +++- drivers/net/wireless/ath/ath6kl/target.h | 16 ++- 5 files changed, 140 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h index 83546d76d979..96851d5df24b 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.h +++ b/drivers/net/wireless/ath/ath6kl/bmi.h @@ -139,8 +139,8 @@ */ #define TARGET_VERSION_SENTINAL 0xffffffff -#define TARGET_TYPE_AR6003 3 - +#define TARGET_TYPE_AR6003 3 +#define TARGET_TYPE_AR6004 5 #define BMI_ROMPATCH_INSTALL 9 /* * Semantics: Install a ROM Patch. diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 74170229523f..214d1144b9bf 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -74,6 +74,13 @@ #define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \ "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" +/* AR6004 1.0 definitions */ +#define AR6004_REV1_VERSION 0x30000623 +#define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin" +#define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin" +#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin" +#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin" + /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) #define STA_PS_SLEEP BIT(1) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 9d10322eac41..df15bfad6043 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -114,7 +114,9 @@ static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar, u32 addr = 0; if (ar->target_type == TARGET_TYPE_AR6003) - addr = ATH6KL_HI_START_ADDR + item_offset; + addr = ATH6KL_AR6003_HI_START_ADDR + item_offset; + else if (ar->target_type == TARGET_TYPE_AR6004) + addr = ATH6KL_AR6004_HI_START_ADDR + item_offset; return addr; } @@ -127,12 +129,12 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar) /* Fetch the address of the host_app_area_s * instance in the host interest area */ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); - address = TARG_VTOP(address); + address = TARG_VTOP(ar->target_type, address); if (ath6kl_read_reg_diag(ar, &address, &data)) return -EIO; - address = TARG_VTOP(data); + address = TARG_VTOP(ar->target_type, data); host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION; if (ath6kl_access_datadiag(ar, address, (u8 *)&host_app_area, @@ -370,7 +372,7 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) /* the reg dump pointer is copied to the host interest area */ address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); - address = TARG_VTOP(address); + address = TARG_VTOP(ar->target_type, address); /* read RAM location through diagnostic window */ status = ath6kl_read_reg_diag(ar, &address, ®dump_loc); @@ -382,8 +384,7 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n", regdump_loc); - - regdump_loc = TARG_VTOP(regdump_loc); + regdump_loc = TARG_VTOP(ar->target_type, regdump_loc); /* fetch register dump data */ status = ath6kl_access_datadiag(ar, @@ -518,10 +519,14 @@ int ath6kl_configure_target(struct ath6kl *ar) * but possible in theory. */ - if (ar->target_type == TARGET_TYPE_AR6003) { + if (ar->target_type == TARGET_TYPE_AR6003 || + ar->target_type == TARGET_TYPE_AR6004) { if (ar->version.target_ver == AR6003_REV2_VERSION) { param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; ram_reserved_size = AR6003_REV2_RAM_RESERVE_SIZE; + } else if (ar->version.target_ver == AR6004_REV1_VERSION) { + param = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; + ram_reserved_size = AR6004_REV1_RAM_RESERVE_SIZE; } else { param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; ram_reserved_size = AR6003_REV3_RAM_RESERVE_SIZE; @@ -614,7 +619,8 @@ int ath6kl_unavail_ev(struct ath6kl *ar) static u32 ath6kl_get_load_address(u32 target_ver, enum addr_type type) { WARN_ON(target_ver != AR6003_REV2_VERSION && - target_ver != AR6003_REV3_VERSION); + target_ver != AR6003_REV3_VERSION && + target_ver != AR6004_REV1_VERSION); switch (type) { case DATASET_PATCH_ADDR: @@ -664,6 +670,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) case AR6003_REV2_VERSION: filename = AR6003_REV2_BOARD_DATA_FILE; break; + case AR6004_REV1_VERSION: + filename = AR6004_REV1_BOARD_DATA_FILE; + break; default: filename = AR6003_REV3_BOARD_DATA_FILE; break; @@ -684,6 +693,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) case AR6003_REV2_VERSION: filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE; break; + case AR6004_REV1_VERSION: + filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE; + break; default: filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE; break; @@ -707,6 +719,7 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) static int ath6kl_upload_board_file(struct ath6kl *ar) { u32 board_address, board_ext_address, param; + u32 board_data_size, board_ext_data_size; int ret; if (ar->fw_board == NULL) { @@ -715,11 +728,24 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) return ret; } - /* Determine where in Target RAM to write Board Data */ - ath6kl_bmi_read(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_data)), - (u8 *) &board_address, 4); + /* + * Determine where in Target RAM to write Board Data. + * For AR6004, host determine Target RAM address for + * writing board data. + */ + if (ar->target_type == TARGET_TYPE_AR6004) { + board_address = AR6004_REV1_BOARD_DATA_ADDRESS; + ath6kl_bmi_write(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); + } else { + ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_data)), + (u8 *) &board_address, 4); + } + ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n", board_address); @@ -737,13 +763,28 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) return -EINVAL; } - if (ar->fw_board_len == (AR6003_BOARD_DATA_SZ + - AR6003_BOARD_EXT_DATA_SZ)) { + switch (ar->target_type) { + case TARGET_TYPE_AR6003: + board_data_size = AR6003_BOARD_DATA_SZ; + board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ; + break; + case TARGET_TYPE_AR6004: + board_data_size = AR6004_BOARD_DATA_SZ; + board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ; + break; + default: + WARN_ON(1); + return -EINVAL; + break; + } + + if (ar->fw_board_len == (board_data_size + + board_ext_data_size)) { + /* write extended board data */ ret = ath6kl_bmi_write(ar, board_ext_address, - ar->fw_board + AR6003_BOARD_DATA_SZ, - AR6003_BOARD_EXT_DATA_SZ); - + ar->fw_board + board_data_size, + board_ext_data_size); if (ret) { ath6kl_err("Failed to write extended board data: %d\n", ret); @@ -751,21 +792,22 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) } /* record that extended board data is initialized */ - param = (AR6003_BOARD_EXT_DATA_SZ << 16) | 1; + param = (board_ext_data_size << 16) | 1; + ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_ext_data_config)), (unsigned char *) ¶m, 4); } - if (ar->fw_board_len < AR6003_BOARD_DATA_SZ) { + if (ar->fw_board_len < board_data_size) { ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); ret = -EINVAL; return ret; } ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, - AR6003_BOARD_DATA_SZ); + board_data_size); if (ret) { ath6kl_err("Board file bmi write failed: %d\n", ret); @@ -792,6 +834,10 @@ static int ath6kl_upload_otp(struct ath6kl *ar) case AR6003_REV2_VERSION: filename = AR6003_REV2_OTP_FILE; break; + case AR6004_REV1_VERSION: + ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); + return 0; + break; default: filename = AR6003_REV3_OTP_FILE; break; @@ -836,6 +882,9 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) case AR6003_REV2_VERSION: filename = AR6003_REV2_FIRMWARE_FILE; break; + case AR6004_REV1_VERSION: + filename = AR6004_REV1_FIRMWARE_FILE; + break; default: filename = AR6003_REV3_FIRMWARE_FILE; break; @@ -860,11 +909,15 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) return ret; } - /* Set starting address for firmware */ - address = ath6kl_get_load_address(ar->version.target_ver, - APP_START_OVERRIDE_ADDR); - ath6kl_bmi_set_app_start(ar, address); - + /* + * Set starting address for firmware + * Don't need to setup app_start override addr on AR6004 + */ + if (ar->target_type != TARGET_TYPE_AR6004) { + address = ath6kl_get_load_address(ar->version.target_ver, + APP_START_OVERRIDE_ADDR); + ath6kl_bmi_set_app_start(ar, address); + } return ret; } @@ -878,6 +931,10 @@ static int ath6kl_upload_patch(struct ath6kl *ar) case AR6003_REV2_VERSION: filename = AR6003_REV2_PATCH_FILE; break; + case AR6004_REV1_VERSION: + /* FIXME: implement for AR6004 */ + return 0; + break; default: filename = AR6003_REV3_PATCH_FILE; break; @@ -916,7 +973,8 @@ static int ath6kl_init_upload(struct ath6kl *ar) u32 param, options, sleep, address; int status = 0; - if (ar->target_type != TARGET_TYPE_AR6003) + if (ar->target_type != TARGET_TYPE_AR6003 && + ar->target_type != TARGET_TYPE_AR6004) return -EINVAL; /* temporarily disable system sleep */ @@ -948,18 +1006,22 @@ static int ath6kl_init_upload(struct ath6kl *ar) options, sleep); /* program analog PLL register */ - status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, - 0xF9104001); - if (status) - return status; + /* no need to control 40/44MHz clock on AR6004 */ + if (ar->target_type != TARGET_TYPE_AR6004) { + status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, + 0xF9104001); - /* Run at 80/88MHz by default */ - param = SM(CPU_CLOCK_STANDARD, 1); + if (status) + return status; - address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; - status = ath6kl_bmi_reg_write(ar, address, param); - if (status) - return status; + /* Run at 80/88MHz by default */ + param = SM(CPU_CLOCK_STANDARD, 1); + + address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + } param = 0; address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index c336eae0cf48..f236aa8c6b8f 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -298,6 +298,10 @@ int ath6kl_access_datadiag(struct ath6kl *ar, u32 address, return status; } +/* FIXME: move to a better place, target.h? */ +#define AR6003_RESET_CONTROL_ADDRESS 0x00004000 +#define AR6004_RESET_CONTROL_ADDRESS 0x00004000 + static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, bool wait_fot_compltn, bool cold_reset) { @@ -305,12 +309,24 @@ static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, u32 address; u32 data; - if (target_type != TARGET_TYPE_AR6003) + if (target_type != TARGET_TYPE_AR6003 && + target_type != TARGET_TYPE_AR6004) return; data = cold_reset ? RESET_CONTROL_COLD_RST : RESET_CONTROL_MBOX_RST; - address = RTC_BASE_ADDRESS; + switch (target_type) { + case TARGET_TYPE_AR6003: + address = AR6003_RESET_CONTROL_ADDRESS; + break; + case TARGET_TYPE_AR6004: + address = AR6004_RESET_CONTROL_ADDRESS; + break; + default: + address = AR6003_RESET_CONTROL_ADDRESS; + break; + } + status = ath6kl_write_reg_diag(ar, &address, &data); if (status) diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 519a013c9991..53e2c786f8e3 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -20,6 +20,9 @@ #define AR6003_BOARD_DATA_SZ 1024 #define AR6003_BOARD_EXT_DATA_SZ 768 +#define AR6004_BOARD_DATA_SZ 7168 +#define AR6004_BOARD_EXT_DATA_SZ 0 + #define RESET_CONTROL_ADDRESS 0x00000000 #define RESET_CONTROL_COLD_RST 0x00000100 #define RESET_CONTROL_MBOX_RST 0x00000004 @@ -135,7 +138,8 @@ * between the two, and is intended to remain constant (with additions only * at the end). */ -#define ATH6KL_HI_START_ADDR 0x00540600 +#define ATH6KL_AR6003_HI_START_ADDR 0x00540600 +#define ATH6KL_AR6004_HI_START_ADDR 0x00400800 /* * These are items that the Host may need to access @@ -314,7 +318,12 @@ struct host_interest { #define HI_OPTION_FW_MODE_SHIFT 0xC /* Convert a Target virtual address into a Target physical address */ -#define TARG_VTOP(vaddr) (vaddr & 0x001fffff) +#define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff) +#define AR6004_VTOP(vaddr) (vaddr) + +#define TARG_VTOP(target_type, vaddr) \ + (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \ + (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0)) #define AR6003_REV2_APP_START_OVERRIDE 0x944C00 #define AR6003_REV2_APP_LOAD_ADDRESS 0x543180 @@ -328,4 +337,7 @@ struct host_interest { #define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74 #define AR6003_REV3_RAM_RESERVE_SIZE 512 +#define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400 +#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000 +#define AR6004_REV1_RAM_RESERVE_SIZE 11264 #endif From f91db9bbdae6c86f0178fa03937e39ef82932770 Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Fri, 12 Aug 2011 17:52:23 +0530 Subject: [PATCH 003/155] ath6kl: Avoid finding bss presence in cfg80211 scan list Connect event handler function is always reporting BSS info to CFG80211 layer first and then followed by connect event is passed. Before these steps, BSS presence is retrieved from CFG80211 layer, but it is not used. Hence, removing that part. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 201398ec4b82..e88b519ed1b6 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -425,8 +425,6 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, unsigned char *ptr_ie_buf = ie_buf; unsigned char *ieeemgmtbuf = NULL; u8 source_mac[ETH_ALEN]; - u16 capa_mask; - u16 capa_val; /* capinfo + listen interval */ u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); @@ -459,24 +457,6 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, } } - if (nw_type & ADHOC_NETWORK) { - capa_mask = WLAN_CAPABILITY_IBSS; - capa_val = WLAN_CAPABILITY_IBSS; - } else { - capa_mask = WLAN_CAPABILITY_ESS; - capa_val = WLAN_CAPABILITY_ESS; - } - - /* Before informing the join/connect event, make sure that - * bss entry is present in scan list, if it not present - * construct and insert into scan list, otherwise that - * event will be dropped on the way by cfg80211, due to - * this keys will not be plumbed in case of WEP and - * application will not be aware of join/connect status. */ - bss = cfg80211_get_bss(ar->wdev->wiphy, NULL, bssid, - ar->wdev->ssid, ar->wdev->ssid_len, - capa_mask, capa_val); - /* * Earlier we were updating the cfg about bss by making a beacon frame * only if the entry for bss is not there. This can have some issue if @@ -527,7 +507,6 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, ieeemgmtbuf = kzalloc(size, GFP_ATOMIC); if (!ieeemgmtbuf) { ath6kl_err("ieee mgmt buf alloc error\n"); - cfg80211_put_bss(bss); return; } From 0e5cc8e606ed89a4a58260c88474c74348230bed Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Fri, 12 Aug 2011 17:52:24 +0530 Subject: [PATCH 004/155] ath6kl: Check sme state before delivering disconnect event to cfg80211 In some random cases, the firmware is sending two disconnect event to the host. In the current model, both diconnect events are passed to cfg80211 without checking local sme state machine, which is screwing cfg80211 layer state. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e88b519ed1b6..b2b70e6618f5 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -643,7 +643,7 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); - } else { + } else if (ar->sme_state == SME_CONNECTED) { cfg80211_disconnected(ar->net_dev, reason, NULL, 0, GFP_KERNEL); } From 65d2bb14ac44e8191beefa8756addd8505224b4a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 14 Aug 2011 18:10:03 -0700 Subject: [PATCH 005/155] ath6kl: fix indentation in htc_issued_send() One line used space to indent. Oddly enough checkpatch didn't complain about this. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index a8dc5c3ea567..20016602dfd8 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -196,7 +196,7 @@ static int htc_issue_send(struct htc_target *target, struct htc_packet *packet) HIF_WR_SYNC_BLOCK_INC); packet->status = status; - packet->buf += HTC_HDR_LENGTH; + packet->buf += HTC_HDR_LENGTH; } else status = hif_write_async(target->dev->ar, target->dev->ar->mbox_info.htc_addr, From 83dc5f2f93adae8907fa105e15a792d860f6affe Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sun, 14 Aug 2011 17:08:33 +0530 Subject: [PATCH 006/155] ath6kl: Release ar->lock right afer updating net_stats in ath6kl_rx() This lock is intended to protect stats there, not neccessary to hold it beyond that. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 167bdb9cf68d..d546051e5953 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1044,13 +1044,13 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ar->net_stats.rx_packets++; ar->net_stats.rx_bytes += packet->act_len; + spin_unlock_bh(&ar->lock); + skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len); - spin_unlock_bh(&ar->lock); - skb->dev = ar->net_dev; if (!test_bit(WMI_ENABLED, &ar->flag)) { From 67f9178fd93d40b72e2db2909f74ead070437317 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sun, 14 Aug 2011 17:08:34 +0530 Subject: [PATCH 007/155] ath6kl: Minor cleanup in min_hdr_len computation Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index d546051e5953..fb67c248f815 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1065,9 +1065,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) return; } - min_hdr_len = sizeof(struct ethhdr); - min_hdr_len += sizeof(struct wmi_data_hdr) + - sizeof(struct ath6kl_llc_snap_hdr); + min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) + + sizeof(struct ath6kl_llc_snap_hdr); dhdr = (struct wmi_data_hdr *) skb->data; From 594a0bc85e3c2ffb17fc8c64a5121fa441c2d096 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sun, 14 Aug 2011 17:08:35 +0530 Subject: [PATCH 008/155] ath6kl: Cleanup ath6kl_wmi_data_hdr_remove() Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 3 +-- drivers/net/wireless/ath/ath6kl/wmi.c | 10 ---------- drivers/net/wireless/ath/ath6kl/wmi.h | 1 - 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index fb67c248f815..5d3d4b61ec89 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1162,8 +1162,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) seq_no = wmi_data_hdr_get_seqno(dhdr); meta_type = wmi_data_hdr_get_meta(dhdr); dot11_hdr = wmi_data_hdr_get_dot11(dhdr); - - ath6kl_wmi_data_hdr_remove(ar->wmi, skb); + skb_pull(skb, sizeof(struct wmi_data_hdr)); switch (meta_type) { case WMI_META_VERSION_1: diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index f5aa33dd4c42..13b1a20cef09 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -376,16 +376,6 @@ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb) return 0; } -int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb) -{ - if (WARN_ON(skb == NULL)) - return -EINVAL; - - skb_pull(skb, sizeof(struct wmi_data_hdr)); - - return 0; -} - static void ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(struct sk_buff *skb, u8 *datap) { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index fe3ddce64087..8fa5d6e46f0e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1925,7 +1925,6 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb); -int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb); int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb, u32 layer2_priority, bool wmm_enabled, u8 *ac); From 13e34ea1f4461007ee300c185f51c990e4381f40 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 16 Aug 2011 11:19:38 +0530 Subject: [PATCH 009/155] ath6kl: Fix bug in computing AMSU subframe padding This fixes AMSDU rx, otherwise it fails with the following warnings. "802.3 AMSDU frame bound check failed" Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 5d3d4b61ec89..44bf2271b162 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -812,7 +812,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr, /* Add the length of A-MSDU subframe padding bytes - * Round to nearest word. */ - frame_8023_len = ALIGN(frame_8023_len + 3, 3); + frame_8023_len = ALIGN(frame_8023_len, 4); framep += frame_8023_len; amsdu_len -= frame_8023_len; From 1df94a8578eb099d9362cc0b84ef85015c47bbc5 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 17 Aug 2011 18:45:10 +0530 Subject: [PATCH 010/155] ath6kl: Fix buffer alignment for scatter-gather I/O For non-scatter buffers, there is already a bounce buffer which takes care of alignment. This patch is influenced by a rough patch of Kalle. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/init.c | 2 +- drivers/net/wireless/ath/ath6kl/main.c | 2 +- drivers/net/wireless/ath/ath6kl/sdio.c | 25 +++++++++++++++---------- drivers/net/wireless/ath/ath6kl/txrx.c | 2 ++ 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 214d1144b9bf..a1aa2ef398f7 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -42,6 +42,9 @@ #define ATH6KL_MAX_ENDPOINTS 4 #define MAX_NODE_NUM 15 +/* Extra bytes for htc header alignment */ +#define ATH6KL_HTC_ALIGN_BYTES 3 + /* MAX_HI_COOKIE_NUM are reserved for high priority traffic */ #define MAX_DEF_COOKIE_NUM 180 #define MAX_HI_COOKIE_NUM 18 /* 10% of MAX_COOKIE_NUM */ diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index df15bfad6043..75230ac28537 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -67,7 +67,7 @@ struct sk_buff *ath6kl_buf_alloc(int size) /* Add chacheline space at front and back of buffer */ reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + - sizeof(struct htc_packet); + sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES; skb = dev_alloc_skb(size + reserved); if (skb) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index f236aa8c6b8f..868838bb6b88 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1347,7 +1347,7 @@ void init_netdev(struct net_device *dev) dev->needed_headroom = ETH_HLEN; dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH - + WMI_MAX_TX_META_SZ; + + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES; return; } diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 34171604cbe4..f393090ecefe 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -128,6 +128,17 @@ static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card, return mmc_wait_for_cmd(card->host, &io_cmd, 0); } +static void ath6kl_sdio_buf_align(u8 **buf, unsigned long len) +{ + u8 *align_addr; + + if (!IS_ALIGNED((unsigned long) *buf, 4)) { + align_addr = PTR_ALIGN(*buf - 4, 4); + memmove(align_addr, *buf, len); + *buf = align_addr; + } +} + static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, u8 *buf, u32 len) { @@ -213,16 +224,10 @@ static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, /* assemble SG list */ for (i = 0; i < scat_req->scat_entries; i++, sg++) { - if ((unsigned long)scat_req->scat_list[i].buf & 0x3) - /* - * Some scatter engines can handle unaligned - * buffers, print this as informational only. - */ - ath6kl_dbg(ATH6KL_DBG_SCATTER, - "(%s) scatter buffer is unaligned 0x%p\n", - scat_req->req & HIF_WRITE ? "WR" : "RD", - scat_req->scat_list[i].buf); - + /* No header is added to rx buf, so it shoule be aligned */ + if (data->flags == MMC_DATA_WRITE) + ath6kl_sdio_buf_align(&scat_req->scat_list[i].buf, + scat_req->scat_list[i].len); ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", i, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 44bf2271b162..ba1350d939a7 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -689,6 +689,7 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) break; packet = (struct htc_packet *) skb->head; + skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); list_add_tail(&packet->list, &queue); @@ -709,6 +710,7 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) return; packet = (struct htc_packet *) skb->head; + skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); spin_lock_bh(&ar->lock); From abcb344b3b823c8c9eac6e13e45a53eaf1d5d00b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 22 Jul 2011 08:26:20 +0300 Subject: [PATCH 011/155] ath6kl: implement suspend support For now this is implemented so that if host supports power is kept in the chip. If that's not supported, an error is returned and sdio stack will remove the device during suspend. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 14 +++++++++ drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/hif-ops.h | 5 ++++ drivers/net/wireless/ath/ath6kl/hif.h | 1 + drivers/net/wireless/ath/ath6kl/main.c | 35 ++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/sdio.c | 26 ++++++++++++++++ 6 files changed, 82 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b2b70e6618f5..9128aa3c2b63 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -17,6 +17,7 @@ #include "core.h" #include "cfg80211.h" #include "debug.h" +#include "hif-ops.h" #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ @@ -1424,6 +1425,16 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) return 0; } +#ifdef CONFIG_PM +static int ar6k_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wow) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + + return ath6kl_hif_suspend(ar); +} +#endif + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1443,6 +1454,9 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .set_pmksa = ath6kl_set_pmksa, .del_pmksa = ath6kl_del_pmksa, .flush_pmksa = ath6kl_flush_pmksa, +#ifdef CONFIG_PM + .suspend = ar6k_cfg80211_suspend, +#endif }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a1aa2ef398f7..4405ab56bb87 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -544,6 +544,7 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid); void ath6kl_dtimexpiry_event(struct ath6kl *ar); void ath6kl_disconnect(struct ath6kl *ar); +void ath6kl_deep_sleep_enable(struct ath6kl *ar); void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid); void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, u8 win_sz); diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index c923979776a0..d6c898f3d0b3 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h @@ -69,4 +69,9 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar) return ar->hif_ops->cleanup_scatter(ar); } +static inline int ath6kl_hif_suspend(struct ath6kl *ar) +{ + return ar->hif_ops->suspend(ar); +} + #endif diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index 5ceff54775a1..797e2d1d9bf9 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -202,6 +202,7 @@ struct ath6kl_hif_ops { int (*scat_req_rw) (struct ath6kl *ar, struct hif_scatter_req *scat_req); void (*cleanup_scatter)(struct ath6kl *ar); + int (*suspend)(struct ath6kl *ar); }; #endif diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 868838bb6b88..b64b2a357560 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -795,6 +795,41 @@ void ath6kl_disconnect(struct ath6kl *ar) } } +void ath6kl_deep_sleep_enable(struct ath6kl *ar) +{ + switch (ar->sme_state) { + case SME_CONNECTING: + cfg80211_connect_result(ar->net_dev, ar->bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + case SME_CONNECTED: + default: + /* + * FIXME: oddly enough smeState is in DISCONNECTED during + * suspend, why? Need to send disconnected event in that + * state. + */ + cfg80211_disconnected(ar->net_dev, 0, NULL, 0, GFP_KERNEL); + break; + } + + if (test_bit(CONNECTED, &ar->flag) || + test_bit(CONNECT_PEND, &ar->flag)) + ath6kl_wmi_disconnect_cmd(ar->wmi); + + ar->sme_state = SME_DISCONNECTED; + + /* disable scanning */ + if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0) != 0) + printk(KERN_WARNING "ath6kl: failed to disable scan " + "during suspend\n"); + + ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED); +} + /* WMI Event handlers */ static const char *get_hw_id_string(u32 id) diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index f393090ecefe..852a0ccc8033 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -726,6 +726,31 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) return 0; } +static int ath6kl_sdio_suspend(struct ath6kl *ar) +{ + struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); + struct sdio_func *func = ar_sdio->func; + mmc_pm_flag_t flags; + int ret; + + flags = sdio_get_host_pm_caps(func); + + if (!(flags & MMC_PM_KEEP_POWER)) + /* as host doesn't support keep power we need to bail out */ + return -EINVAL; + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n", + ret); + return ret; + } + + ath6kl_deep_sleep_enable(ar); + + return 0; +} + static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .read_write_sync = ath6kl_sdio_read_write_sync, .write_async = ath6kl_sdio_write_async, @@ -736,6 +761,7 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = { .enable_scatter = ath6kl_sdio_enable_scatter, .scat_req_rw = ath6kl_sdio_async_rw_scatter, .cleanup_scatter = ath6kl_sdio_cleanup_scatter, + .suspend = ath6kl_sdio_suspend, }; static int ath6kl_sdio_probe(struct sdio_func *func, From 94e532d1a053b1514ffdad00408eee925104bf27 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 22 Aug 2011 20:14:31 +0530 Subject: [PATCH 012/155] ath6kl: Fix system freeze under heavy data load Patch "ath6kl: Fix buffer alignment for scatter-gather write" does memmove for a length (scat_req->scat_list[i].len) which is not the actual length of data that is suppossed to be moved. The right lengh is packet->act_len + HTC_HDR_LENGTH. Using wrong length for data move during buffer alignment causes system freeze after the following WARN_ON and sometimes target assert. WARNING: at drivers/net/wireless/ath/ath6kl/main.c:771 ath6k_credit_distribute+0x196/0x1a0 [] ath6kl_htc_rxmsg_pending_handler+0x83f/0xe00 [ath6kl] [] ? __wake_up+0x53/0x70 [] ath6kldev_intr_bh_handler+0x188/0x650 [ath6kl] [] ath6kl_sdio_irq_handler+0x36/0x80 [ath6kl] [] sdio_irq_thread+0xfc/0x360 [] ? default_wake_function+0x12/0x20 [] ? sdio_claim_irq+0x220/0x220 [] kthread+0x96/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? kthread_worker_fn+0x190/0x190 [] ? gs_change+0x13/0x13 Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 14 ++++++++++++++ drivers/net/wireless/ath/ath6kl/sdio.c | 15 --------------- drivers/net/wireless/ath/ath6kl/txrx.c | 6 ++++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index 20016602dfd8..dc575a82af16 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -22,6 +22,17 @@ #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) +static void ath6kl_htc_buf_align(u8 **buf, unsigned long len) +{ + u8 *align_addr; + + if (!IS_ALIGNED((unsigned long) *buf, 4)) { + align_addr = PTR_ALIGN(*buf - 4, 4); + memmove(align_addr, *buf, len); + *buf = align_addr; + } +} + static void htc_prep_send_pkt(struct htc_packet *packet, u8 flags, int ctrl0, int ctrl1) { @@ -391,6 +402,9 @@ static int htc_setup_send_scat_list(struct htc_target *target, htc_prep_send_pkt(packet, packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, cred_pad, packet->info.tx.seqno); + /* Make sure the buffer is 4-byte aligned */ + ath6kl_htc_buf_align(&packet->buf, + packet->act_len + HTC_HDR_LENGTH); scat_req->scat_list[i].buf = packet->buf; scat_req->scat_list[i].len = len; diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 852a0ccc8033..0cce80169670 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -128,17 +128,6 @@ static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card, return mmc_wait_for_cmd(card->host, &io_cmd, 0); } -static void ath6kl_sdio_buf_align(u8 **buf, unsigned long len) -{ - u8 *align_addr; - - if (!IS_ALIGNED((unsigned long) *buf, 4)) { - align_addr = PTR_ALIGN(*buf - 4, 4); - memmove(align_addr, *buf, len); - *buf = align_addr; - } -} - static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, u8 *buf, u32 len) { @@ -224,10 +213,6 @@ static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, /* assemble SG list */ for (i = 0; i < scat_req->scat_entries; i++, sg++) { - /* No header is added to rx buf, so it shoule be aligned */ - if (data->flags == MMC_DATA_WRITE) - ath6kl_sdio_buf_align(&scat_req->scat_list[i].buf, - scat_req->scat_list[i].len); ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", i, scat_req->scat_list[i].buf, scat_req->scat_list[i].len); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index ba1350d939a7..ba33370ca9aa 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -689,7 +689,8 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) break; packet = (struct htc_packet *) skb->head; - skb->data = PTR_ALIGN(skb->data - 4, 4); + if (!IS_ALIGNED((unsigned long) skb->data, 4)) + skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); list_add_tail(&packet->list, &queue); @@ -710,7 +711,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) return; packet = (struct htc_packet *) skb->head; - skb->data = PTR_ALIGN(skb->data - 4, 4); + if (!IS_ALIGNED((unsigned long) skb->data, 4)) + skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); spin_lock_bh(&ar->lock); From 8af123e8ee272ad175440891333602d8d4b8e63c Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 22 Aug 2011 20:40:20 +0530 Subject: [PATCH 013/155] ath6kl: Remove unused meta_v2 from ath6kl_data_tx() Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index ba33370ca9aa..fffd92920d35 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -239,7 +239,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) u16 htc_tag = ATH6KL_DATA_PKT_TAG; u8 ac = 99 ; /* initialize to unmapped ac */ bool chk_adhoc_ps_mapping = false, more_data = false; - struct wmi_tx_meta_v2 meta_v2; int ret; ath6kl_dbg(ATH6KL_DBG_WLAN_TX, @@ -262,8 +261,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) } if (test_bit(WMI_ENABLED, &ar->flag)) { - memset(&meta_v2, 0, sizeof(meta_v2)); - if (skb_headroom(skb) < dev->needed_headroom) { WARN_ON(1); goto fail_tx; From 3ce6ff501c92e15314f450edc2e93653a7325780 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 22 Aug 2011 20:40:21 +0530 Subject: [PATCH 014/155] ath6kl: Add wmi meta data information only it is available Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 13b1a20cef09..d116d0e337de 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -167,9 +167,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb, if (WARN_ON(skb == NULL)) return -EINVAL; - ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info); - if (ret) - return ret; + if (tx_meta_info) { + ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info); + if (ret) + return ret; + } skb_push(skb, sizeof(struct wmi_data_hdr)); From f7a7e7ae5db1d436805de7fe19c51b5b2657c63e Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 22 Aug 2011 20:40:22 +0530 Subject: [PATCH 015/155] ath6kl: Avoid rolling back of entire scatter setup in case of failure Current tx scatter gather implementation rolls back the entire scatter setup in case of a failure in setting up just one packet into the bundle. Instead of dopping the whole scatter setup, send the packets available just before the failure one using scatter gather I/O. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index dc575a82af16..77e548ce2c03 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -381,13 +381,7 @@ static int htc_setup_send_scat_list(struct htc_target *target, cred_pad = htc_get_credit_padding(target->tgt_cred_sz, &len, endpoint); - if (cred_pad < 0) { - status = -EINVAL; - break; - } - - if (rem_scat < len) { - /* exceeds what we can transfer */ + if (cred_pad < 0 || rem_scat < len) { status = -ENOSPC; break; } @@ -416,7 +410,7 @@ static int htc_setup_send_scat_list(struct htc_target *target, } /* Roll back scatter setup in case of any failure */ - if (status || (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE)) { + if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) { for (i = scat_req->scat_entries - 1; i >= 0; i--) { packet = scat_req->scat_list[i].packet; if (packet) { @@ -424,10 +418,10 @@ static int htc_setup_send_scat_list(struct htc_target *target, list_add(&packet->list, queue); } } - return -EINVAL; + return -EAGAIN; } - return 0; + return status; } /* @@ -447,8 +441,10 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, struct htc_target *target = endpoint->target; struct hif_scatter_req *scat_req = NULL; int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0; + int status; while (true) { + status = 0; n_scat = get_queue_depth(queue); n_scat = min(n_scat, target->msg_per_bndl_max); @@ -471,8 +467,9 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, scat_req->len = 0; scat_req->scat_entries = 0; - if (htc_setup_send_scat_list(target, endpoint, scat_req, - n_scat, queue)) { + status = htc_setup_send_scat_list(target, endpoint, + scat_req, n_scat, queue); + if (status == -EAGAIN) { hif_scatter_req_add(target->dev->ar, scat_req); break; } @@ -486,6 +483,9 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, "send scatter total bytes: %d , entries: %d\n", scat_req->len, scat_req->scat_entries); ath6kldev_submit_scat_req(target->dev, scat_req, false); + + if (status) + break; } *sent_bundle = n_sent_bundle; From d999ba3e21dc1c84cac9caf68db78fd6dbde7817 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 26 Aug 2011 13:06:31 +0530 Subject: [PATCH 016/155] ath6kl: Add initial debugfs changes Just initial debugfs changes. The debugfs directory would be created at /ieee80211/phyX/ath6kl. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/debug.c | 10 ++++++++++ drivers/net/wireless/ath/ath6kl/debug.h | 6 +++++- drivers/net/wireless/ath/ath6kl/init.c | 6 ++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 4405ab56bb87..c5537b3f77c8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -467,6 +467,7 @@ struct ath6kl { struct workqueue_struct *ath6kl_wq; struct ath6kl_node_table scan_table; + struct dentry *debugfs_phy; }; static inline void *ath6kl_priv(struct net_device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 316136c8b903..12775e80a0f4 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -147,4 +147,14 @@ void dump_cred_dist_stats(struct htc_target *target) target->cred_dist_cntxt->cur_free_credits); } +int ath6kl_debug_init(struct ath6kl *ar) +{ + ar->debugfs_phy = debugfs_create_dir("ath6kl", + ar->wdev->wiphy->debugfsdir); + if (!ar->debugfs_phy) + return -ENOMEM; + + /* TODO: Create debugfs file entries for various target/host stats */ + return 0; +} #endif diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 66b399962f01..e8c9ea9ce02c 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -78,6 +78,7 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_en_reg); void dump_cred_dist_stats(struct htc_target *target); +int ath6kl_debug_init(struct ath6kl *ar); #else static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, const char *fmt, ...) @@ -100,6 +101,9 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev, static inline void dump_cred_dist_stats(struct htc_target *target) { } +static inline int ath6kl_debug_init(struct ath6kl *ar) +{ + return 0; +} #endif - #endif diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 75230ac28537..ad9716c91a81 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -573,6 +573,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev) ar->wdev = wdev; wdev->iftype = NL80211_IFTYPE_STATION; + if (ath6kl_debug_init(ar)) { + ath6kl_err("Failed to initialize debugfs\n"); + ath6kl_cfg80211_deinit(ar); + return NULL; + } + dev = alloc_netdev(0, "wlan%d", ether_setup); if (!dev) { ath6kl_err("no memory for network device instance\n"); From 03f68a95e5763faf7b95993b3407fb816c200893 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 26 Aug 2011 13:06:32 +0530 Subject: [PATCH 017/155] ath6kl: Add debugfs entry to dump target stats It would be at /ieee80211/phyX/ath6kl/tgt_stats. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.c | 147 +++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 12775e80a0f4..5a082c0f34cd 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -147,6 +147,149 @@ void dump_cred_dist_stats(struct htc_target *target) target->cred_dist_cntxt->cur_free_credits); } +static int ath6kl_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct target_stats *tgt_stats = &ar->target_stats; + char *buf; + unsigned int len = 0, buf_len = 1500; + int i; + long left; + ssize_t ret_cnt; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (down_interruptible(&ar->sem)) { + kfree(buf); + return -EBUSY; + } + + set_bit(STATS_UPDATE_PEND, &ar->flag); + + if (ath6kl_wmi_get_stats_cmd(ar->wmi)) { + up(&ar->sem); + kfree(buf); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + !test_bit(STATS_UPDATE_PEND, + &ar->flag), WMI_TIMEOUT); + + up(&ar->sem); + + if (left <= 0) { + kfree(buf); + return -ETIMEDOUT; + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Target Tx stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast packets", tgt_stats->tx_ucast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast packets", tgt_stats->tx_bcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast byte", tgt_stats->tx_ucast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast byte", tgt_stats->tx_bcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Rts success cnt", tgt_stats->tx_rts_success_cnt); + for (i = 0; i < 4; i++) + len += scnprintf(buf + len, buf_len - len, + "%18s %d %10llu\n", "PER on ac", + i, tgt_stats->tx_pkt_per_ac[i]); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Error", tgt_stats->tx_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Fail count", tgt_stats->tx_fail_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Retry count", tgt_stats->tx_retry_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Multi retry cnt", tgt_stats->tx_mult_retry_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Rts fail cnt", tgt_stats->tx_rts_fail_cnt); + len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n", + "TKIP counter measure used", + tgt_stats->tkip_cnter_measures_invoked); + + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Target Rx stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast packets", tgt_stats->rx_ucast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "Ucast Rate", tgt_stats->rx_ucast_rate); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast packets", tgt_stats->rx_bcast_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Ucast byte", tgt_stats->rx_ucast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Bcast byte", tgt_stats->rx_bcast_byte); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Fragmented pkt", tgt_stats->rx_frgment_pkt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Error", tgt_stats->rx_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "CRC Err", tgt_stats->rx_crc_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Key chache miss", tgt_stats->rx_key_cache_miss); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Decrypt Err", tgt_stats->rx_decrypt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Duplicate frame", tgt_stats->rx_dupl_frame); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Tkip Mic failure", tgt_stats->tkip_local_mic_fail); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "TKIP format err", tgt_stats->tkip_fmt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "CCMP format Err", tgt_stats->ccmp_fmt_err); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n", + "CCMP Replay Err", tgt_stats->ccmp_replays); + + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Misc Target stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Beacon Miss count", tgt_stats->cs_bmiss_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Num Connects", tgt_stats->cs_connect_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", + "Num disconnects", tgt_stats->cs_discon_cnt); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_tgt_stats = { + .read = read_file_tgt_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath6kl_debug_init(struct ath6kl *ar) { ar->debugfs_phy = debugfs_create_dir("ath6kl", @@ -154,7 +297,9 @@ int ath6kl_debug_init(struct ath6kl *ar) if (!ar->debugfs_phy) return -ENOMEM; - /* TODO: Create debugfs file entries for various target/host stats */ + debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_tgt_stats); + return 0; } #endif From 78fc485622240f812ad95bba5a2165f4e7c77b54 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 26 Aug 2011 13:06:33 +0530 Subject: [PATCH 018/155] ath6kl: Add debugfs file entry to dump credit distribution stats It would be at /ieee80211/phyX/ath6kl/credit_dist_stats. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.c | 69 +++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 5a082c0f34cd..2b462876cec1 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -290,6 +290,72 @@ static const struct file_operations fops_tgt_stats = { .llseek = default_llseek, }; +#define print_credit_info(fmt_str, ep_list_field) \ + (len += scnprintf(buf + len, buf_len - len, fmt_str, \ + ep_list->ep_list_field)) +#define CREDIT_INFO_DISPLAY_STRING_LEN 200 +#define CREDIT_INFO_LEN 128 + +static ssize_t read_file_credit_dist_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct htc_target *target = ar->htc_target; + struct htc_endpoint_credit_dist *ep_list; + char *buf; + unsigned int buf_len, len = 0; + ssize_t ret_cnt; + + buf_len = CREDIT_INFO_DISPLAY_STRING_LEN + + get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN; + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", + "Total Avail Credits: ", + target->cred_dist_cntxt->total_avail_credits); + len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", + "Free credits :", + target->cred_dist_cntxt->cur_free_credits); + + len += scnprintf(buf + len, buf_len - len, + " Epid Flags Cred_norm Cred_min Credits Cred_assngd" + " Seek_cred Cred_sz Cred_per_msg Cred_to_dist" + " qdepth\n"); + + list_for_each_entry(ep_list, &target->cred_dist_list, list) { + print_credit_info(" %2d", endpoint); + print_credit_info("%10x", dist_flags); + print_credit_info("%8d", cred_norm); + print_credit_info("%9d", cred_min); + print_credit_info("%9d", credits); + print_credit_info("%10d", cred_assngd); + print_credit_info("%13d", seek_cred); + print_credit_info("%12d", cred_sz); + print_credit_info("%9d", cred_per_msg); + print_credit_info("%14d", cred_to_dist); + len += scnprintf(buf + len, buf_len - len, "%12d\n", + get_queue_depth(&((struct htc_endpoint *) + ep_list->htc_rsvd)->txq)); + } + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_credit_dist_stats = { + .read = read_file_credit_dist_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath6kl_debug_init(struct ath6kl *ar) { ar->debugfs_phy = debugfs_create_dir("ath6kl", @@ -300,6 +366,9 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_tgt_stats); + debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_credit_dist_stats); + return 0; } #endif From 6a7c9badab158086b6162c661a47c4f1a4a68e92 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:50 +0300 Subject: [PATCH 019/155] ath6kl: Add functionality for starting AP mode Use cfg80211 add/del_beacon callbacks for starting/stopping AP mode and set_beacon to update AP configuration (mainly, to update Beacon and Probe Response IEs). Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 179 +++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 2 + drivers/net/wireless/ath/ath6kl/wmi.c | 45 ++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 20 +++ 4 files changed, 246 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 9128aa3c2b63..4752a76e13cd 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1435,6 +1435,181 @@ static int ar6k_cfg80211_suspend(struct wiphy *wiphy, } #endif +static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + if (!ath6kl_cfg80211_ready(ar)) + return -EIO; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", + __func__, chan->center_freq, chan->hw_value); + ar->next_chan = chan->center_freq; + + return 0; +} + +static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info, bool add) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ieee80211_mgmt *mgmt; + u8 *ies; + int ies_len; + struct wmi_connect_cmd p; + int res; + int i; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); + + if (!ath6kl_cfg80211_ready(ar)) + return -EIO; + + if (ar->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + if (info->beacon_ies) { + res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON, + info->beacon_ies, + info->beacon_ies_len); + if (res) + return res; + } + if (info->proberesp_ies) { + res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP, + info->proberesp_ies, + info->proberesp_ies_len); + if (res) + return res; + } + if (info->assocresp_ies) { + res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP, + info->assocresp_ies, + info->assocresp_ies_len); + if (res) + return res; + } + + if (!add) + return 0; + + /* TODO: + * info->interval + * info->dtim_period + */ + + if (info->head == NULL) + return -EINVAL; + mgmt = (struct ieee80211_mgmt *) info->head; + ies = mgmt->u.beacon.variable; + if (ies > info->head + info->head_len) + return -EINVAL; + ies_len = info->head + info->head_len - ies; + + if (info->ssid == NULL) + return -EINVAL; + memcpy(ar->ssid, info->ssid, info->ssid_len); + ar->ssid_len = info->ssid_len; + if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) + return -EOPNOTSUPP; /* TODO */ + + ar->dot11_auth_mode = OPEN_AUTH; + + memset(&p, 0, sizeof(p)); + + for (i = 0; i < info->crypto.n_akm_suites; i++) { + switch (info->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) + p.auth_mode |= WPA_AUTH; + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) + p.auth_mode |= WPA2_AUTH; + break; + case WLAN_AKM_SUITE_PSK: + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) + p.auth_mode |= WPA_PSK_AUTH; + if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) + p.auth_mode |= WPA2_PSK_AUTH; + break; + } + } + if (p.auth_mode == 0) + p.auth_mode = NONE_AUTH; + ar->auth_mode = p.auth_mode; + + for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) { + switch (info->crypto.ciphers_pairwise[i]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + p.prwise_crypto_type |= WEP_CRYPT; + break; + case WLAN_CIPHER_SUITE_TKIP: + p.prwise_crypto_type |= TKIP_CRYPT; + break; + case WLAN_CIPHER_SUITE_CCMP: + p.prwise_crypto_type |= AES_CRYPT; + break; + } + } + if (p.prwise_crypto_type == 0) + p.prwise_crypto_type = NONE_CRYPT; + + switch (info->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + p.grp_crypto_type = WEP_CRYPT; + break; + case WLAN_CIPHER_SUITE_TKIP: + p.grp_crypto_type = TKIP_CRYPT; + break; + case WLAN_CIPHER_SUITE_CCMP: + p.grp_crypto_type = AES_CRYPT; + break; + default: + p.grp_crypto_type = NONE_CRYPT; + break; + } + + p.nw_type = AP_NETWORK; + ar->nw_type = ar->next_mode; + + p.ssid_len = ar->ssid_len; + memcpy(p.ssid, ar->ssid, ar->ssid_len); + p.dot11_auth_mode = ar->dot11_auth_mode; + p.ch = cpu_to_le16(ar->next_chan); + + return ath6kl_wmi_ap_profile_commit(ar->wmi, &p); +} + +static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + return ath6kl_ap_beacon(wiphy, dev, info, true); +} + +static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + return ath6kl_ap_beacon(wiphy, dev, info, false); +} + +static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + if (ar->nw_type != AP_NETWORK) + return -EOPNOTSUPP; + if (!test_bit(CONNECTED, &ar->flag)) + return -ENOTCONN; + + ath6kl_wmi_disconnect_cmd(ar->wmi); + clear_bit(CONNECTED, &ar->flag); + + return 0; +} + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1457,6 +1632,10 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { #ifdef CONFIG_PM .suspend = ar6k_cfg80211_suspend, #endif + .set_channel = ath6kl_set_channel, + .add_beacon = ath6kl_add_beacon, + .set_beacon = ath6kl_set_beacon, + .del_beacon = ath6kl_del_beacon, }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c5537b3f77c8..00d0add2ab46 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -468,6 +468,8 @@ struct ath6kl { struct ath6kl_node_table scan_table; struct dentry *debugfs_phy; + + u16 next_chan; }; static inline void *ath6kl_priv(struct net_device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index d116d0e337de..0114a7136977 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1399,6 +1399,8 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb, enum htc_endpoint_id ep_id = wmi->ep_id; int ret; + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: cmd_id=%d\n", __func__, cmd_id); + if (WARN_ON(skb == NULL)) return -EINVAL; @@ -2392,6 +2394,29 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len) } /* AP mode functions */ + +int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p) +{ + struct sk_buff *skb; + struct wmi_connect_cmd *cm; + int res; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); + if (!skb) + return -ENOMEM; + + cm = (struct wmi_connect_cmd *) skb->data; + memcpy(cm, p, sizeof(*cm)); + + res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID, + NO_SYNC_WMIFLAG); + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u " + "ctrl_flags=0x%x-> res=%d\n", + __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch), + le32_to_cpu(p->ctrl_flags), res); + return res; +} + static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_pspoll_event *ev; @@ -2456,6 +2481,26 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver, return ret; } +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie, + u8 ie_len) +{ + struct sk_buff *skb; + struct wmi_set_appie_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u " + "ie_len=%u\n", mgmt_frm_type, ie_len); + p = (struct wmi_set_appie_cmd *) skb->data; + p->mgmt_frm_type = mgmt_frm_type; + p->ie_len = ie_len; + memcpy(p->ie_info, ie, ie_len); + return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID, + NO_SYNC_WMIFLAG); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 8fa5d6e46f0e..6bdfd4a86111 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -503,6 +503,15 @@ enum wmi_cmd_id { WMI_SET_EXCESS_TX_RETRY_THRES_CMDID, }; +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ, + WMI_FRAME_PROBE_RESP, + WMI_FRAME_ASSOC_REQ, + WMI_FRAME_ASSOC_RESP, + WMI_NUM_MGMT_FRAME +}; + /* WMI_CONNECT_CMDID */ enum network_type { INFRA_NETWORK = 0x01, @@ -1642,6 +1651,12 @@ struct wmi_get_keepalive_cmd { u8 keep_alive_intvl; } __packed; +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 ie_len; + u8 ie_info[0]; +} __packed; + /* Notify the WSC registration status to the target */ #define WSC_REG_ACTIVE 1 #define WSC_REG_INACTIVE 0 @@ -2006,11 +2021,16 @@ struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid, void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss); /* AP mode */ +int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p); + int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag); int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version, bool rx_dot11_hdr, bool defrag_on_host); +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie, + u8 ie_len); + void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); From 3c774bbab78435e349de2c88fc6e054716f8f2ea Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:51 +0300 Subject: [PATCH 020/155] ath6kl: Fix AP mode (Re)AssocReq IE processing Need to use correct length field for association request frame and parse the IEs to find WPA/WPS/RSN IE. In addition, copying of the IE better make sure it fits in into the buffer to avoid buffer overflows. In addition, add the (Re)AssocReq IEs to the cfg80211 new station event for user space. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 57 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index b64b2a357560..89e29ead254f 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -61,7 +61,8 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie, sta = &ar->sta_list[free_slot]; memcpy(sta->mac, mac, ETH_ALEN); - memcpy(sta->wpa_ie, wpaie, ielen); + if (ielen <= ATH6KL_MAX_IE) + memcpy(sta->wpa_ie, wpaie, ielen); sta->aid = aid; sta->keymgmt = keymgmt; sta->ucipher = ucipher; @@ -429,9 +430,11 @@ static void ath6kl_install_static_wep_keys(struct ath6kl *ar) static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, - u8 assoc_resp_len, u8 *assoc_info) + u8 assoc_req_len, u8 *assoc_info) { struct net_device *dev = ar->net_dev; + u8 *ies = NULL, *wpa_ie = NULL, *pos; + size_t ies_len = 0; struct station_info sinfo; struct ath6kl_req_key *ik; enum crypto_type keyType = NONE_CRYPT; @@ -473,7 +476,43 @@ skip_key: ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", bssid, channel); - ath6kl_add_new_sta(ar, bssid, channel, assoc_info, assoc_resp_len, + if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) { + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *) assoc_info; + if (ieee80211_is_assoc_req(mgmt->frame_control) && + assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt->u.assoc_req)) { + ies = mgmt->u.assoc_req.variable; + ies_len = assoc_info + assoc_req_len - ies; + } else if (ieee80211_is_reassoc_req(mgmt->frame_control) && + assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt->u.reassoc_req)) { + ies = mgmt->u.reassoc_req.variable; + ies_len = assoc_info + assoc_req_len - ies; + } + } + + pos = ies; + while (pos && pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if (pos[0] == WLAN_EID_RSN) + wpa_ie = pos; /* RSN IE */ + else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) { + if (pos[5] == 0x01) + wpa_ie = pos; /* WPA IE */ + else if (pos[5] == 0x04) { + wpa_ie = pos; /* WPS IE */ + break; /* overrides WPA/RSN IE */ + } + } + pos += 2 + pos[1]; + } + + ath6kl_add_new_sta(ar, bssid, channel, wpa_ie, + wpa_ie ? 2 + wpa_ie[1] : 0, listen_int & 0xFF, beacon_int, (listen_int >> 8) & 0xFF); @@ -481,9 +520,11 @@ skip_key: memset(&sinfo, 0, sizeof(sinfo)); /* TODO: sinfo.generation */ - /* TODO: need to deliver (Re)AssocReq IEs somehow.. change in - * cfg80211 needed, e.g., by adding those into sinfo - */ + + sinfo.assoc_req_ies = ies; + sinfo.assoc_req_ies_len = ies_len; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + cfg80211_new_sta(ar->net_dev, bssid, &sinfo, GFP_KERNEL); netif_wake_queue(ar->net_dev); @@ -895,8 +936,8 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, if (ar->nw_type == AP_NETWORK) { ath6kl_connect_ap_mode(ar, channel, bssid, listen_int, - beacon_int, assoc_resp_len, - assoc_info); + beacon_int, assoc_req_len, + assoc_info + beacon_ie_len); return; } From 9a5b13182cc10d693c55a5c02d753e54514b9bfc Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:52 +0300 Subject: [PATCH 021/155] ath6kl: Delay initial group key setup in AP mode The target is not ready to accept addkey commands until the connect event has been delivered, so delay these operations for the initial GTK. In addition, properly set interface connected and mark netdev ready when the AP mode setup has been completed. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 31 ++++++++++++++++++- drivers/net/wireless/ath/ath6kl/core.h | 25 ++++----------- drivers/net/wireless/ath/ath6kl/main.c | 36 ++++++++++++---------- drivers/net/wireless/ath/ath6kl/wmi.c | 4 +++ 4 files changed, 60 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 4752a76e13cd..faefc23750d3 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -888,6 +888,26 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, key_usage, key->seq_len); ar->def_txkey_index = key_index; + + if (ar->nw_type == AP_NETWORK && !pairwise && + (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) { + ar->ap_mode_bkey.valid = true; + ar->ap_mode_bkey.key_index = key_index; + ar->ap_mode_bkey.key_type = key_type; + ar->ap_mode_bkey.key_len = key->key_len; + memcpy(ar->ap_mode_bkey.key, key->key, key->key_len); + if (!test_bit(CONNECTED, &ar->flag)) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group " + "key configuration until AP mode has been " + "started\n"); + /* + * The key will be set in ath6kl_connect_ap_mode() once + * the connected event is received from the target. + */ + return 0; + } + } + status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, key_type, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, @@ -997,6 +1017,9 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, if (ar->prwise_crypto == WEP_CRYPT) key_usage |= TX_USAGE; + if (ar->nw_type == AP_NETWORK && !test_bit(CONNECTED, &ar->flag)) + return 0; /* Delay until AP mode has been started */ + status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, ar->prwise_crypto, key_usage, key->key_len, key->seq, key->key, @@ -1495,6 +1518,8 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, if (!add) return 0; + ar->ap_mode_bkey.valid = false; + /* TODO: * info->interval * info->dtim_period @@ -1580,7 +1605,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, p.dot11_auth_mode = ar->dot11_auth_mode; p.ch = cpu_to_le16(ar->next_chan); - return ath6kl_wmi_ap_profile_commit(ar->wmi, &p); + res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p); + if (res < 0) + return res; + + return 0; } static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 00d0add2ab46..f0b1dff1ce09 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -335,26 +335,13 @@ struct ath6kl_mbox_info { #define ATH6KL_KEY_RECV 0x02 #define ATH6KL_KEY_DEFAULT 0x80 /* default xmit key */ -/* - * WPA/RSN get/set key request. Specify the key/cipher - * type and whether the key is to be used for sending and/or - * receiving. The key index should be set only when working - * with global keys (use IEEE80211_KEYIX_NONE for ``no index''). - * Otherwise a unicast/pairwise key is specified by the bssid - * (on a station) or mac address (on an ap). They key length - * must include any MIC key data; otherwise it should be no - * more than ATH6KL_KEYBUF_SIZE. - */ +/* Initial group key for AP mode */ struct ath6kl_req_key { - u8 ik_type; /* key/cipher type */ - u8 ik_pad; - u16 ik_keyix; /* key index */ - u8 ik_keylen; /* key length in bytes */ - u8 ik_flags; - u8 ik_macaddr[ETH_ALEN]; - u64 ik_keyrsc; /* key receive sequence counter */ - u64 ik_keytsc; /* key transmit sequence counter */ - u8 ik_keydata[ATH6KL_KEYBUF_SIZE + ATH6KL_MICBUF_SIZE]; + bool valid; + u8 key_index; + int key_type; + u8 key[WLAN_MAX_KEY_LEN]; + u8 key_len; }; /* Flag info */ diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 89e29ead254f..a19caecdfdeb 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -437,11 +437,15 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, size_t ies_len = 0; struct station_info sinfo; struct ath6kl_req_key *ik; - enum crypto_type keyType = NONE_CRYPT; + int res; + u8 key_rsc[ATH6KL_KEY_SEQ_LEN]; if (memcmp(dev->dev_addr, bssid, ETH_ALEN) == 0) { ik = &ar->ap_mode_bkey; + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", + channel); + switch (ar->auth_mode) { case NONE_AUTH: if (ar->prwise_crypto == WEP_CRYPT) @@ -450,26 +454,26 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, case WPA_PSK_AUTH: case WPA2_PSK_AUTH: case (WPA_PSK_AUTH|WPA2_PSK_AUTH): - switch (ik->ik_type) { - case ATH6KL_CIPHER_TKIP: - keyType = TKIP_CRYPT; + if (!ik->valid) break; - case ATH6KL_CIPHER_AES_CCM: - keyType = AES_CRYPT; - break; - default: - goto skip_key; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for " + "the initial group key for AP mode\n"); + memset(key_rsc, 0, sizeof(key_rsc)); + res = ath6kl_wmi_addkey_cmd( + ar->wmi, ik->key_index, ik->key_type, + GROUP_USAGE, ik->key_len, key_rsc, ik->key, + KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); + if (res) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed " + "addkey failed: %d\n", res); } - ath6kl_wmi_addkey_cmd(ar->wmi, ik->ik_keyix, keyType, - GROUP_USAGE, ik->ik_keylen, - (u8 *)&ik->ik_keyrsc, - ik->ik_keydata, - KEY_OP_INIT_VAL, ik->ik_macaddr, - SYNC_BOTH_WMIFLAG); break; } -skip_key: + + ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &ar->flag); + netif_carrier_on(ar->net_dev); return; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 0114a7136977..d587f84b41cf 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1764,6 +1764,10 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index, struct wmi_add_cipher_key_cmd *cmd; int ret; + ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d " + "key_usage=%d key_len=%d key_op_ctrl=%d\n", + key_index, key_type, key_usage, key_len, key_op_ctrl); + if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) || (key_material == NULL)) return -EINVAL; From 238751365a1c42b1d66beb03dd81ca5d0fd12833 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:53 +0300 Subject: [PATCH 022/155] ath6kl: Use change_station() to authorize/unauthorize STAs Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 20 ++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.c | 18 ++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index faefc23750d3..f7176d203be0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1639,6 +1639,25 @@ static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) return 0; } +static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + if (ar->nw_type != AP_NETWORK) + return -EOPNOTSUPP; + + /* Use this only for authorizing/unauthorizing a station */ + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return -EOPNOTSUPP; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE, + mac, 0); + return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac, + 0); +} + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1665,6 +1684,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .add_beacon = ath6kl_add_beacon, .set_beacon = ath6kl_set_beacon, .del_beacon = ath6kl_del_beacon, + .change_station = ath6kl_change_station, }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index d587f84b41cf..1a3991ce8cfa 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2421,6 +2421,24 @@ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p) return res; } +int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason) +{ + struct sk_buff *skb; + struct wmi_ap_set_mlme_cmd *cm; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cm)); + if (!skb) + return -ENOMEM; + + cm = (struct wmi_ap_set_mlme_cmd *) skb->data; + memcpy(cm->mac, mac, ETH_ALEN); + cm->reason = cpu_to_le16(reason); + cm->cmd = cmd; + + return ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_SET_MLME_CMDID, + NO_SYNC_WMIFLAG); +} + static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_pspoll_event *ev; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 6bdfd4a86111..eb6bfcd879e0 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1804,6 +1804,23 @@ struct wmi_tx_complete_event { /* Used with WMI_AP_SET_NUM_STA_CMDID */ +/* + * Used with WMI_AP_SET_MLME_CMDID + */ + +/* MLME Commands */ +#define WMI_AP_MLME_ASSOC 1 /* associate station */ +#define WMI_AP_DISASSOC 2 /* disassociate station */ +#define WMI_AP_DEAUTH 3 /* deauthenticate station */ +#define WMI_AP_MLME_AUTHORIZE 4 /* authorize station */ +#define WMI_AP_MLME_UNAUTHORIZE 5 /* unauthorize station */ + +struct wmi_ap_set_mlme_cmd { + u8 mac[ETH_ALEN]; + __le16 reason; /* 802.11 reason code */ + u8 cmd; /* operation to perform (WMI_AP_*) */ +} __packed; + struct wmi_ap_set_pvb_cmd { __le32 flag; __le16 aid; @@ -2023,6 +2040,8 @@ void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss); /* AP mode */ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p); +int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason); + int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag); int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version, From 6465ddcf6c1e06d3fde870624be4418e747f0e8b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:54 +0300 Subject: [PATCH 023/155] ath6kl: Add new WMI commands and events for P2P Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 294 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 184 +++++++++++++++- 2 files changed, 470 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 1a3991ce8cfa..261ccff0a647 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -425,6 +425,148 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) return 0; } +static int ath6kl_wmi_remain_on_chnl_event_rx(u8 *datap, int len) +{ + struct wmi_remain_on_chnl_event *ev; + u32 freq; + u32 dur; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_remain_on_chnl_event *) datap; + freq = le32_to_cpu(ev->freq); + dur = le32_to_cpu(ev->duration); + ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n", + freq, dur); + + return 0; +} + +static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(u8 *datap, int len) +{ + struct wmi_cancel_remain_on_chnl_event *ev; + u32 freq; + u32 dur; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_cancel_remain_on_chnl_event *) datap; + freq = le32_to_cpu(ev->freq); + dur = le32_to_cpu(ev->duration); + ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u " + "status=%u\n", freq, dur, ev->status); + + return 0; +} + +static int ath6kl_wmi_tx_status_event_rx(u8 *datap, int len) +{ + struct wmi_tx_status_event *ev; + u32 id; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_tx_status_event *) datap; + id = le32_to_cpu(ev->id); + ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", + id, ev->ack_status); + + return 0; +} + +static int ath6kl_wmi_rx_probe_req_event_rx(u8 *datap, int len) +{ + struct wmi_p2p_rx_probe_req_event *ev; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_rx_probe_req_event *) datap; + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u\n", + dlen); + + return 0; +} + +static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len) +{ + struct wmi_p2p_capabilities_event *ev; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_capabilities_event *) datap; + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen); + + return 0; +} + +static int ath6kl_wmi_rx_action_event_rx(u8 *datap, int len) +{ + struct wmi_rx_action_event *ev; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_rx_action_event *) datap; + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u\n", dlen); + + return 0; +} + +static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len) +{ + struct wmi_p2p_info_event *ev; + u32 flags; + u16 dlen; + + if (len < sizeof(*ev)) + return -EINVAL; + + ev = (struct wmi_p2p_info_event *) datap; + flags = le32_to_cpu(ev->info_req_flags); + dlen = le16_to_cpu(ev->len); + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen); + + if (flags & P2P_FLAG_CAPABILITIES_REQ) { + struct wmi_p2p_capabilities *cap; + if (dlen < sizeof(*cap)) + return -EINVAL; + cap = (struct wmi_p2p_capabilities *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n", + cap->go_power_save); + } + + if (flags & P2P_FLAG_MACADDR_REQ) { + struct wmi_p2p_macaddr *mac; + if (dlen < sizeof(*mac)) + return -EINVAL; + mac = (struct wmi_p2p_macaddr *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n", + mac->mac_addr); + } + + if (flags & P2P_FLAG_HMODEL_REQ) { + struct wmi_p2p_hmodel *mod; + if (dlen < sizeof(*mod)) + return -EINVAL; + mod = (struct wmi_p2p_hmodel *) ev->data; + ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n", + mod->p2p_model, + mod->p2p_model ? "host" : "firmware"); + } + return 0; +} + static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size) { struct sk_buff *skb; @@ -2523,6 +2665,129 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie, NO_SYNC_WMIFLAG); } +int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) +{ + struct sk_buff *skb; + struct wmi_disable_11b_rates_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n", + disable); + cmd = (struct wmi_disable_11b_rates_cmd *) skb->data; + cmd->disable = disable ? 1 : 0; + + return ath6kl_wmi_cmd_send(wmi, skb, WMI_DISABLE_11B_RATES_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur) +{ + struct sk_buff *skb; + struct wmi_remain_on_chnl_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n", + freq, dur); + p = (struct wmi_remain_on_chnl_cmd *) skb->data; + p->freq = cpu_to_le32(freq); + p->duration = cpu_to_le32(dur); + return ath6kl_wmi_cmd_send(wmi, skb, WMI_REMAIN_ON_CHNL_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait, + const u8 *data, u16 data_len) +{ + struct sk_buff *skb; + struct wmi_send_action_cmd *p; + + if (wait) + return -EINVAL; /* Offload for wait not supported */ + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u " + "len=%u\n", id, freq, wait, data_len); + p = (struct wmi_send_action_cmd *) skb->data; + p->id = cpu_to_le32(id); + p->freq = cpu_to_le32(freq); + p->wait = cpu_to_le32(wait); + p->len = cpu_to_le16(data_len); + memcpy(p->data, data, data_len); + return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_ACTION_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq, + const u8 *dst, + const u8 *data, u16 data_len) +{ + struct sk_buff *skb; + struct wmi_p2p_probe_response_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM " + "len=%u\n", freq, dst, data_len); + p = (struct wmi_p2p_probe_response_cmd *) skb->data; + p->freq = cpu_to_le32(freq); + memcpy(p->destination_addr, dst, ETH_ALEN); + p->len = cpu_to_le16(data_len); + memcpy(p->data, data, data_len); + return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_PROBE_RESPONSE_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable) +{ + struct sk_buff *skb; + struct wmi_probe_req_report_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n", + enable); + p = (struct wmi_probe_req_report_cmd *) skb->data; + p->enable = enable ? 1 : 0; + return ath6kl_wmi_cmd_send(wmi, skb, WMI_PROBE_REQ_REPORT_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags) +{ + struct sk_buff *skb; + struct wmi_get_p2p_info *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n", + info_req_flags); + p = (struct wmi_get_p2p_info *) skb->data; + p->info_req_flags = cpu_to_le32(info_req_flags); + return ath6kl_wmi_cmd_send(wmi, skb, WMI_GET_P2P_INFO_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi) +{ + ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n"); + return ath6kl_wmi_simple_cmd(wmi, WMI_CANCEL_REMAIN_ON_CHNL_CMDID); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; @@ -2742,6 +3007,35 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n"); ret = ath6kl_wmi_tx_complete_event_rx(datap, len); break; + case WMI_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_remain_on_chnl_event_rx(datap, len); + break; + case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, + "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); + ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(datap, len); + break; + case WMI_TX_STATUS_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); + ret = ath6kl_wmi_tx_status_event_rx(datap, len); + break; + case WMI_RX_PROBE_REQ_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); + ret = ath6kl_wmi_rx_probe_req_event_rx(datap, len); + break; + case WMI_P2P_CAPABILITIES_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); + ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len); + break; + case WMI_RX_ACTION_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); + ret = ath6kl_wmi_rx_action_event_rx(datap, len); + break; + case WMI_P2P_INFO_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); + ret = ath6kl_wmi_p2p_info_event_rx(datap, len); + break; default: ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id); wmi->stat.cmd_id_err++; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index eb6bfcd879e0..5e2f6ce41ba1 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -490,17 +490,52 @@ enum wmi_cmd_id { WMI_SET_PASSPHRASE_CMDID, WMI_SEND_ASSOC_RES_CMDID, WMI_SET_ASSOC_REQ_RELAY_CMDID, - WMI_GET_RFKILL_MODE_CMDID, /* ACS command, consists of sub-commands */ WMI_ACS_CTRL_CMDID, + WMI_SET_EXCESS_TX_RETRY_THRES_CMDID, + WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */ + /* Pktlog cmds */ + WMI_PKTLOG_ENABLE_CMDID, + WMI_PKTLOG_DISABLE_CMDID, + + /* More P2P Cmds */ + WMI_P2P_GO_NEG_REQ_RSP_CMDID, + WMI_P2P_GRP_INIT_CMDID, + WMI_P2P_GRP_FORMATION_DONE_CMDID, + WMI_P2P_INVITE_CMDID, + WMI_P2P_INVITE_REQ_RSP_CMDID, + WMI_P2P_PROV_DISC_REQ_CMDID, + WMI_P2P_SET_CMDID, + + WMI_GET_RFKILL_MODE_CMDID, + WMI_SET_RFKILL_MODE_CMDID, + WMI_AP_SET_APSD_CMDID, + WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID, + + WMI_P2P_SDPD_TX_CMDID, /* F05C */ + WMI_P2P_STOP_SDPD_CMDID, + WMI_P2P_CANCEL_CMDID, /* Ultra low power store / recall commands */ WMI_STORERECALL_CONFIGURE_CMDID, WMI_STORERECALL_RECALL_CMDID, WMI_STORERECALL_HOST_READY_CMDID, WMI_FORCE_TARGET_ASSERT_CMDID, - WMI_SET_EXCESS_TX_RETRY_THRES_CMDID, + + WMI_SET_PROBED_SSID_EX_CMDID, + WMI_SET_NETWORK_LIST_OFFLOAD_CMDID, + WMI_SET_ARP_NS_OFFLOAD_CMDID, + WMI_ADD_WOW_EXT_PATTERN_CMDID, + WMI_GTK_OFFLOAD_OP_CMDID, + WMI_REMAIN_ON_CHNL_CMDID, + WMI_CANCEL_REMAIN_ON_CHNL_CMDID, + WMI_SEND_ACTION_CMDID, + WMI_PROBE_REQ_REPORT_CMDID, + WMI_DISABLE_11B_RATES_CMDID, + WMI_SEND_PROBE_RESPONSE_CMDID, + WMI_GET_P2P_INFO_CMDID, + WMI_AP_JOIN_BSS_CMDID, }; enum wmi_mgmt_frame_type { @@ -1188,15 +1223,26 @@ enum wmi_event_id { WMI_WAC_START_WPS_EVENTID, WMI_WAC_CTRL_REQ_REPLY_EVENTID, + WMI_REPORT_WMM_PARAMS_EVENTID, + WMI_WAC_REJECT_WPS_EVENTID, + + /* More P2P Events */ + WMI_P2P_GO_NEG_REQ_EVENTID, + WMI_P2P_INVITE_REQ_EVENTID, + WMI_P2P_INVITE_RCVD_RESULT_EVENTID, + WMI_P2P_INVITE_SENT_RESULT_EVENTID, + WMI_P2P_PROV_DISC_RESP_EVENTID, + WMI_P2P_PROV_DISC_REQ_EVENTID, + /* RFKILL Events */ WMI_RFKILL_STATE_CHANGE_EVENTID, WMI_RFKILL_GET_MODE_CMD_EVENTID, - WMI_THIN_RESERVED_START_EVENTID = 0x8000, - /* - * Events in this range are reserved for thinmode - * See wmi_thin.h for actual definitions - */ + WMI_P2P_START_SDPD_EVENTID, + WMI_P2P_SDPD_RX_EVENTID, + + WMI_THIN_RESERVED_START_EVENTID = 0x8000, + /* Events in this range are reserved for thinmode */ WMI_THIN_RESERVED_END_EVENTID = 0x8fff, WMI_SET_CHANNEL_EVENTID, @@ -1204,7 +1250,17 @@ enum wmi_event_id { /* Generic ACS event */ WMI_ACS_EVENTID, - WMI_REPORT_WMM_PARAMS_EVENTID + WMI_STORERECALL_STORE_EVENTID, + WMI_WOW_EXT_WAKE_EVENTID, + WMI_GTK_OFFLOAD_STATUS_EVENTID, + WMI_NETWORK_LIST_OFFLOAD_EVENTID, + WMI_REMAIN_ON_CHNL_EVENTID, + WMI_CANCEL_REMAIN_ON_CHNL_EVENTID, + WMI_TX_STATUS_EVENTID, + WMI_RX_PROBE_REQ_EVENTID, + WMI_P2P_CAPABILITIES_EVENTID, + WMI_RX_ACTION_EVENTID, + WMI_P2P_INFO_EVENTID, }; struct wmi_ready_event_2 { @@ -1872,6 +1928,100 @@ struct wmi_ap_mode_stat { /* End of AP mode definitions */ +struct wmi_remain_on_chnl_cmd { + __le32 freq; + __le32 duration; +} __packed; + +struct wmi_send_action_cmd { + __le32 id; + __le32 freq; + __le32 wait; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_tx_status_event { + __le32 id; + u8 ack_status; +} __packed; + +struct wmi_probe_req_report_cmd { + u8 enable; +} __packed; + +struct wmi_disable_11b_rates_cmd { + u8 disable; +} __packed; + +struct wmi_set_appie_extended_cmd { + u8 role_id; + u8 mgmt_frm_type; + u8 ie_len; + u8 ie_info[0]; +} __packed; + +struct wmi_remain_on_chnl_event { + __le32 freq; + __le32 duration; +} __packed; + +struct wmi_cancel_remain_on_chnl_event { + __le32 freq; + __le32 duration; + u8 status; +} __packed; + +struct wmi_rx_action_event { + __le32 freq; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_capabilities_event { + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_rx_probe_req_event { + __le32 freq; + __le16 len; + u8 data[0]; +} __packed; + +#define P2P_FLAG_CAPABILITIES_REQ (0x00000001) +#define P2P_FLAG_MACADDR_REQ (0x00000002) +#define P2P_FLAG_HMODEL_REQ (0x00000002) + +struct wmi_get_p2p_info { + __le32 info_req_flags; +} __packed; + +struct wmi_p2p_info_event { + __le32 info_req_flags; + __le16 len; + u8 data[0]; +} __packed; + +struct wmi_p2p_capabilities { + u8 go_power_save; +} __packed; + +struct wmi_p2p_macaddr { + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct wmi_p2p_hmodel { + u8 p2p_model; +} __packed; + +struct wmi_p2p_probe_response_cmd { + __le32 freq; + u8 destination_addr[ETH_ALEN]; + __le16 len; + u8 data[0]; +} __packed; + /* Extended WMI (WMIX) * * Extended WMIX commands are encapsulated in a WMI message with @@ -2050,6 +2200,24 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version, int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); +/* P2P */ +int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); + +int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur); + +int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait, + const u8 *data, u16 data_len); + +int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq, + const u8 *dst, + const u8 *data, u16 data_len); + +int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable); + +int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags); + +int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi); + void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); From 63fa1e0ca7a2c1e0cbf5f39b866340127ddc1480 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:55 +0300 Subject: [PATCH 024/155] ath6kl: Implement remain_on_channel and cancel_remain_on_channel Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f7176d203be0..6745bf203ec5 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1658,6 +1658,35 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, 0); } +static int ath6kl_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, + u64 *cookie) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + /* TODO: if already pending or ongoing remain-on-channel, + * return -EBUSY */ + *cookie = 1; /* only a single pending request is supported */ + + return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq, + duration); +} + +static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + if (cookie != 1) + return -ENOENT; + + return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi); +} + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1685,6 +1714,8 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .set_beacon = ath6kl_set_beacon, .del_beacon = ath6kl_del_beacon, .change_station = ath6kl_change_station, + .remain_on_channel = ath6kl_remain_on_channel, + .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) @@ -1706,6 +1737,8 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) return NULL; } + wdev->wiphy->max_remain_on_channel_duration = 5000; + /* set device pointer for wiphy */ set_wiphy_dev(wdev->wiphy, dev); From 8a6c8060c0b166ce5ce4a3563b511b1f641dbea8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:56 +0300 Subject: [PATCH 025/155] ath6kl: Implement mgmt_tx Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 24 ++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 1 + 2 files changed, 25 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 6745bf203ec5..5c98de36d163 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1687,6 +1687,29 @@ static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi); } +static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 *buf, size_t len, u64 *cookie) +{ + struct ath6kl *ar = ath6kl_priv(dev); + u32 id; + + id = ar->send_action_id++; + if (id == 0) { + /* + * 0 is a reserved value in the WMI command and shall not be + * used for the command. + */ + id = ar->send_action_id++; + } + + *cookie = id; + return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait, + buf, len); +} + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1716,6 +1739,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, + .mgmt_tx = ath6kl_mgmt_tx, }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index f0b1dff1ce09..3872edbe0597 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -456,6 +456,7 @@ struct ath6kl { struct ath6kl_node_table scan_table; struct dentry *debugfs_phy; + u32 send_action_id; u16 next_chan; }; From 4dea08e07e2103f183bf3a316c80e80950412ca5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:57 +0300 Subject: [PATCH 026/155] ath6kl: Request P2P capabilities during target init Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index ad9716c91a81..48c82e9561bf 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -417,6 +417,7 @@ void ath6kl_target_failure(struct ath6kl *ar) static int ath6kl_target_config_wlan_params(struct ath6kl *ar) { int status = 0; + int ret; /* * Configure the device for rx dot11 header rules. "0,0" are the @@ -461,6 +462,15 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar) status = -EIO; } + ret = ath6kl_wmi_info_req_cmd(ar->wmi, P2P_FLAG_CAPABILITIES_REQ | + P2P_FLAG_MACADDR_REQ | + P2P_FLAG_HMODEL_REQ); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " + "capabilities (%d) - assuming P2P not supported\n", + ret); + } + return status; } From f9e5f05cb9c944696def27618215216df59c7c33 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:58 +0300 Subject: [PATCH 027/155] ath6kl: Add cfg80211 calls for remain-on-channel events Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 31 +++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 261ccff0a647..2d80bdb2d912 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -425,11 +425,14 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) return 0; } -static int ath6kl_wmi_remain_on_chnl_event_rx(u8 *datap, int len) +static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, + int len) { struct wmi_remain_on_chnl_event *ev; u32 freq; u32 dur; + struct ieee80211_channel *chan; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(*ev)) return -EINVAL; @@ -439,15 +442,26 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(u8 *datap, int len) dur = le32_to_cpu(ev->duration); ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n", freq, dur); + chan = ieee80211_get_channel(ar->wdev->wiphy, freq); + if (!chan) { + ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel " + "(freq=%u)\n", freq); + return -EINVAL; + } + cfg80211_ready_on_channel(ar->net_dev, 1, chan, NL80211_CHAN_NO_HT, + dur, GFP_ATOMIC); return 0; } -static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(u8 *datap, int len) +static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, + u8 *datap, int len) { struct wmi_cancel_remain_on_chnl_event *ev; u32 freq; u32 dur; + struct ieee80211_channel *chan; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(*ev)) return -EINVAL; @@ -457,6 +471,14 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(u8 *datap, int len) dur = le32_to_cpu(ev->duration); ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u " "status=%u\n", freq, dur, ev->status); + chan = ieee80211_get_channel(ar->wdev->wiphy, freq); + if (!chan) { + ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown " + "channel (freq=%u)\n", freq); + return -EINVAL; + } + cfg80211_remain_on_channel_expired(ar->net_dev, 1, chan, + NL80211_CHAN_NO_HT, GFP_ATOMIC); return 0; } @@ -3009,12 +3031,13 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_REMAIN_ON_CHNL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n"); - ret = ath6kl_wmi_remain_on_chnl_event_rx(datap, len); + ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len); break; case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n"); - ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(datap, len); + ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap, + len); break; case WMI_TX_STATUS_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); From b84da8c738681b96e7691d985191ebf9ee4a21e8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:57:59 +0300 Subject: [PATCH 028/155] ath6kl: Use set_appie command to add Probe Request IEs Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 11 +++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 5c98de36d163..56a60c7f53c1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -776,6 +776,16 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, request->ssids[i].ssid); } + if (request->ie) { + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ, + request->ie, request->ie_len); + if (ret) { + ath6kl_err("failed to set Probe Request appie for " + "scan"); + return ret; + } + } + if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, false, 0, 0, 0, NULL) != 0) { ath6kl_err("wmi_startscan_cmd failed\n"); @@ -1770,6 +1780,7 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) BIT(NL80211_IFTYPE_ADHOC); /* max num of ssids that can be probed during scanning */ wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; + wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 5e2f6ce41ba1..83af518fcafc 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2218,6 +2218,9 @@ int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags); int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi); +int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie, + u8 ie_len); + void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); From 1276c9ef6db2bc856579bc7f02e4cc710b089f0d Mon Sep 17 00:00:00 2001 From: Edward Lu Date: Tue, 30 Aug 2011 21:58:00 +0300 Subject: [PATCH 029/155] ath6kl: Support channel set request for startscan command Signed-off-by: Edward Lu Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 22 +++++++++++++++++++++- drivers/net/wireless/ath/ath6kl/wmi.c | 6 +++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 56a60c7f53c1..78b178892ede 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -748,6 +748,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); + s8 n_channels = 0; + u16 *channels = NULL; int ret = 0; if (!ath6kl_cfg80211_ready(ar)) @@ -786,14 +788,32 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } } + if (request->n_channels > 0) { + u8 i; + + n_channels = min(127U, request->n_channels); + + channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); + if (channels == NULL) { + ath6kl_warn("failed to set scan channels, " + "scan all channels"); + n_channels = 0; + } + + for (i = 0; i < n_channels; i++) + channels[i] = request->channels[i]->center_freq; + } + if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, - false, 0, 0, 0, NULL) != 0) { + false, 0, 0, n_channels, channels) != 0) { ath6kl_err("wmi_startscan_cmd failed\n"); ret = -EIO; } ar->scan_req = request; + kfree(channels); + return ret; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 2d80bdb2d912..bbe3e8d214c8 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1709,7 +1709,7 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type, struct sk_buff *skb; struct wmi_start_scan_cmd *sc; s8 size; - int ret; + int i, ret; size = sizeof(struct wmi_start_scan_cmd); @@ -1734,8 +1734,8 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type, sc->force_scan_intvl = cpu_to_le32(force_scan_interval); sc->num_ch = num_chan; - if (num_chan) - memcpy(sc->ch_list, ch_list, num_chan * sizeof(u16)); + for (i = 0; i < num_chan; i++) + sc->ch_list[i] = cpu_to_le16(ch_list[i]); ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID, NO_SYNC_WMIFLAG); From ae32c30a6ec991088e5346036015be1a9f9cf14b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:01 +0300 Subject: [PATCH 030/155] ath6kl: Report received Probe Request frames to cfg80211 Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 19 +++++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 7 +++++++ drivers/net/wireless/ath/ath6kl/wmi.c | 20 ++++++++++++++++---- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 78b178892ede..60339598ad25 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1740,6 +1740,24 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, buf, len); } +static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, + u16 frame_type, bool reg) +{ + struct ath6kl *ar = ath6kl_priv(dev); + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", + __func__, frame_type, reg); + if (frame_type == IEEE80211_STYPE_PROBE_REQ) { + /* + * Note: This notification callback is not allowed to sleep, so + * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we + * hardcode target to report Probe Request frames all the time. + */ + ar->probe_req_report = reg; + } +} + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1770,6 +1788,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .remain_on_channel = ath6kl_remain_on_channel, .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel, .mgmt_tx = ath6kl_mgmt_tx, + .mgmt_frame_register = ath6kl_mgmt_frame_register, }; struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 3872edbe0597..99cabd251caf 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -457,6 +457,7 @@ struct ath6kl { struct dentry *debugfs_phy; u32 send_action_id; + bool probe_req_report; u16 next_chan; }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 48c82e9561bf..3b99ae2dfb18 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -471,6 +471,13 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar) ret); } + /* Enable Probe Request reporting for P2P */ + ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe Request " + "reporting (%d)\n", ret); + } + return status; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index bbe3e8d214c8..9b2a1829776e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -499,18 +499,30 @@ static int ath6kl_wmi_tx_status_event_rx(u8 *datap, int len) return 0; } -static int ath6kl_wmi_rx_probe_req_event_rx(u8 *datap, int len) +static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_p2p_rx_probe_req_event *ev; + u32 freq; u16 dlen; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_p2p_rx_probe_req_event *) datap; + freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); - ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u\n", - dlen); + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid wmi_p2p_rx_probe_req_event: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u " + "probe_req_report=%d\n", + dlen, freq, ar->probe_req_report); + + if (ar->probe_req_report || ar->nw_type == AP_NETWORK) + cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC); return 0; } @@ -3045,7 +3057,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_RX_PROBE_REQ_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); - ret = ath6kl_wmi_rx_probe_req_event_rx(datap, len); + ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len); break; case WMI_P2P_CAPABILITIES_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n"); From a0df5db15b432cd49319254132fda80cb3081ad6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:02 +0300 Subject: [PATCH 031/155] ath6kl: Notify cfg80211 of TX status of mgmt_tx frames Use WMI_TX_STATUS_EVENTID event to generate cfg80211_mgmt_tx_frame() calls. Since we support only a single pending frame for now, use the hardcoded cookie value 1 and store a copy of the pending frame in the driver. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 30 +++++++++++++++++++++++---- drivers/net/wireless/ath/ath6kl/wmi.h | 3 +++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 9b2a1829776e..d098cbd07fa9 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -483,10 +483,11 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, return 0; } -static int ath6kl_wmi_tx_status_event_rx(u8 *datap, int len) +static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_tx_status_event *ev; u32 id; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(*ev)) return -EINVAL; @@ -495,6 +496,15 @@ static int ath6kl_wmi_tx_status_event_rx(u8 *datap, int len) id = le32_to_cpu(ev->id); ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", id, ev->ack_status); + if (wmi->last_mgmt_tx_frame) { + cfg80211_mgmt_tx_status(ar->net_dev, id, + wmi->last_mgmt_tx_frame, + wmi->last_mgmt_tx_frame_len, + !!ev->ack_status, GFP_ATOMIC); + kfree(wmi->last_mgmt_tx_frame); + wmi->last_mgmt_tx_frame = NULL; + wmi->last_mgmt_tx_frame_len = 0; + } return 0; } @@ -2740,14 +2750,25 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait, { struct sk_buff *skb; struct wmi_send_action_cmd *p; + u8 *buf; if (wait) return -EINVAL; /* Offload for wait not supported */ - skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); - if (!skb) + buf = kmalloc(data_len, GFP_KERNEL); + if (!buf) return -ENOMEM; + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len); + if (!skb) { + kfree(buf); + return -ENOMEM; + } + + kfree(wmi->last_mgmt_tx_frame); + wmi->last_mgmt_tx_frame = buf; + wmi->last_mgmt_tx_frame_len = data_len; + ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u " "len=%u\n", id, freq, wait, data_len); p = (struct wmi_send_action_cmd *) skb->data; @@ -3053,7 +3074,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_TX_STATUS_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n"); - ret = ath6kl_wmi_tx_status_event_rx(datap, len); + ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len); break; case WMI_RX_PROBE_REQ_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n"); @@ -3127,5 +3148,6 @@ void ath6kl_wmi_shutdown(struct wmi *wmi) if (!wmi) return; + kfree(wmi->last_mgmt_tx_frame); kfree(wmi); } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 83af518fcafc..cb3d27afcc65 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -129,6 +129,9 @@ struct wmi { u8 ht_allowed[A_NUM_BANDS]; u8 traffic_class; bool is_probe_ssid; + + u8 *last_mgmt_tx_frame; + size_t last_mgmt_tx_frame_len; }; struct host_app_area { From 9809d8ef274bb53f47998bbc401efcbb10226893 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:03 +0300 Subject: [PATCH 032/155] ath6kl: Report received Action frames to cfg80211 Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index d098cbd07fa9..dec869790c17 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -552,17 +552,26 @@ static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len) return 0; } -static int ath6kl_wmi_rx_action_event_rx(u8 *datap, int len) +static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_rx_action_event *ev; + u32 freq; u16 dlen; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(*ev)) return -EINVAL; ev = (struct wmi_rx_action_event *) datap; + freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); - ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u\n", dlen); + if (datap + len < ev->data + dlen) { + ath6kl_err("invalid wmi_rx_action_event: " + "len=%d dlen=%u\n", len, dlen); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); + cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC); return 0; } @@ -3086,7 +3095,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_RX_ACTION_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); - ret = ath6kl_wmi_rx_action_event_rx(datap, len); + ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len); break; case WMI_P2P_INFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n"); From f80574ae1538f6fb17aeedb005380fd6961e976e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:04 +0300 Subject: [PATCH 033/155] ath6kl: Advertise supported mgmt_stypes Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 60339598ad25..2d443979e9f0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1758,6 +1758,28 @@ static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, } } +static const struct ieee80211_txrx_stypes +ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + static struct cfg80211_ops ath6kl_cfg80211_ops = { .change_virtual_intf = ath6kl_cfg80211_change_iface, .scan = ath6kl_cfg80211_scan, @@ -1810,6 +1832,8 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) return NULL; } + wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes; + wdev->wiphy->max_remain_on_channel_duration = 5000; /* set device pointer for wiphy */ From 6b5e5d257211ee3e3df780488e8d31ce2bd9940f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:05 +0300 Subject: [PATCH 034/155] ath6kl: Add support for new P2P iftypes in mode changes Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 2d443979e9f0..72735ee0ee5e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -451,7 +451,8 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, } if (nw_type & INFRA_NETWORK) { - if (ar->wdev->iftype != NL80211_IFTYPE_STATION) { + if (ar->wdev->iftype != NL80211_IFTYPE_STATION && + ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in station mode\n", __func__); return; @@ -612,7 +613,8 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, } if (ar->nw_type & INFRA_NETWORK) { - if (ar->wdev->iftype != NL80211_IFTYPE_STATION) { + if (ar->wdev->iftype != NL80211_IFTYPE_STATION && + ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in station mode\n", __func__); return; @@ -1206,6 +1208,12 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, case NL80211_IFTYPE_ADHOC: ar->next_mode = ADHOC_NETWORK; break; + case NL80211_IFTYPE_P2P_CLIENT: + ar->next_mode = INFRA_NETWORK; + break; + case NL80211_IFTYPE_P2P_GO: + ar->next_mode = AP_NETWORK; + break; default: ath6kl_err("invalid interface type %u\n", type); return -EOPNOTSUPP; From 38acde3c137919c1aeced3eab0f79bb416f5ad8a Mon Sep 17 00:00:00 2001 From: Edward Lu Date: Tue, 30 Aug 2011 21:58:06 +0300 Subject: [PATCH 035/155] ath6kl: Fix a typo in ath6k context Signed-off-by: Edward Lu Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 13 +++++++------ drivers/net/wireless/ath/ath6kl/core.h | 2 +- drivers/net/wireless/ath/ath6kl/init.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 72735ee0ee5e..af8e9ccb256d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -168,7 +168,8 @@ static int ath6kl_set_auth_type(struct ath6kl *ar, static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast) { u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto; - u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : &ar->grp_crpto_len; + u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : + &ar->grp_crypto_len; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", __func__, cipher, ucast); @@ -371,14 +372,14 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, __func__, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, - ar->grp_crpto_len, ar->ch_hint); + ar->grp_crypto_len, ar->ch_hint); ar->reconnect_flag = 0; status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, ar->dot11_auth_mode, ar->auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, - ar->grp_crypto, ar->grp_crpto_len, + ar->grp_crypto, ar->grp_crypto_len, ar->ssid_len, ar->ssid, ar->req_bssid, ar->ch_hint, ar->connect_ctrl_flags); @@ -688,7 +689,7 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, - ar->grp_crpto_len, + ar->grp_crypto_len, ar->ssid_len, ar->ssid, ar->req_bssid, @@ -1277,13 +1278,13 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, __func__, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, - ar->grp_crpto_len, ar->ch_hint); + ar->grp_crypto_len, ar->ch_hint); status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, ar->dot11_auth_mode, ar->auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, - ar->grp_crypto, ar->grp_crpto_len, + ar->grp_crypto, ar->grp_crypto_len, ar->ssid_len, ar->ssid, ar->req_bssid, ar->ch_hint, ar->connect_ctrl_flags); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 99cabd251caf..60e2291fecc3 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -380,7 +380,7 @@ struct ath6kl { u8 prwise_crypto; u8 prwise_crypto_len; u8 grp_crypto; - u8 grp_crpto_len; + u8 grp_crypto_len; u8 def_txkey_index; struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; u8 bssid[ETH_ALEN]; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 3b99ae2dfb18..32b7ef5e2aca 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -85,7 +85,7 @@ void ath6kl_init_profile_info(struct ath6kl *ar) ar->prwise_crypto = NONE_CRYPT; ar->prwise_crypto_len = 0; ar->grp_crypto = NONE_CRYPT; - ar->grp_crpto_len = 0; + ar->grp_crypto_len = 0; memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list)); memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); memset(ar->bssid, 0, sizeof(ar->bssid)); From 229ed6b55f3caa4f1a975fd297ec44c5cedf4ea0 Mon Sep 17 00:00:00 2001 From: Edward Lu Date: Tue, 30 Aug 2011 21:58:07 +0300 Subject: [PATCH 036/155] ath6kl: Fix default key installation in AP mode Signed-off-by: Edward Lu Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index af8e9ccb256d..a3aa15058947 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1025,6 +1025,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, struct ath6kl_key *key = NULL; int status = 0; u8 key_usage; + enum crypto_type key_type = NONE_CRYPT; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); @@ -1049,12 +1050,16 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, key_usage = GROUP_USAGE; if (ar->prwise_crypto == WEP_CRYPT) key_usage |= TX_USAGE; + if (unicast) + key_type = ar->prwise_crypto; + if (multicast) + key_type = ar->grp_crypto; if (ar->nw_type == AP_NETWORK && !test_bit(CONNECTED, &ar->flag)) return 0; /* Delay until AP mode has been started */ status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, - ar->prwise_crypto, key_usage, + key_type, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); @@ -1617,8 +1622,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, break; } } - if (p.prwise_crypto_type == 0) + if (p.prwise_crypto_type == 0) { p.prwise_crypto_type = NONE_CRYPT; + ath6kl_set_cipher(ar, 0, true); + } else if (info->crypto.n_ciphers_pairwise == 1) + ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true); switch (info->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: @@ -1635,6 +1643,7 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, p.grp_crypto_type = NONE_CRYPT; break; } + ath6kl_set_cipher(ar, info->crypto.cipher_group, false); p.nw_type = AP_NETWORK; ar->nw_type = ar->next_mode; From a587526a44d0c2812ee9d650e7c0626b48697aca Mon Sep 17 00:00:00 2001 From: Edward Lu Date: Tue, 30 Aug 2011 21:58:08 +0300 Subject: [PATCH 037/155] ath6kl: Do not clear CONNECT bit setting in AP mode for STA disconnect Signed-off-by: Edward Lu Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index a19caecdfdeb..69a1b45179c5 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1284,7 +1284,8 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL); } - clear_bit(CONNECTED, &ar->flag); + if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) + clear_bit(CONNECTED, &ar->flag); return; } From 8bdfbf40721a4338eb4f6dec55b3205188c45973 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:09 +0300 Subject: [PATCH 038/155] ath6kl: Include P2P IE(s) in GO Probe Response depending on request P2P has special rules on when to include P2P IE(s) in Probe Response frame based on the Probe Request frame. Handle P2P IE(s) separately to follow these rules. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 96 +++++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a3aa15058947..2136899561df 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1518,6 +1518,48 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, return 0; } +static bool ath6kl_is_p2p_ie(const u8 *pos) +{ + return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + pos[2] == 0x50 && pos[3] == 0x6f && + pos[4] == 0x9a && pos[5] == 0x09; +} + +static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies, + size_t ies_len) +{ + const u8 *pos; + u8 *buf = NULL; + size_t len = 0; + int ret; + + /* + * Filter out P2P IE(s) since they will be included depending on + * the Probe Request frame in ath6kl_send_go_probe_resp(). + */ + + if (ies && ies_len) { + buf = kmalloc(ies_len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + pos = ies; + while (pos + 1 < ies + ies_len) { + if (pos + 2 + pos[1] > ies + ies_len) + break; + if (!ath6kl_is_p2p_ie(pos)) { + memcpy(buf + len, pos, 2 + pos[1]); + len += 2 + pos[1]; + } + pos += 2 + pos[1]; + } + } + + ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP, + buf, len); + kfree(buf); + return ret; +} + static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info, bool add) { @@ -1545,9 +1587,8 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return res; } if (info->proberesp_ies) { - res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP, - info->proberesp_ies, - info->proberesp_ies_len); + res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies, + info->proberesp_ies_len); if (res) return res; } @@ -1735,6 +1776,41 @@ static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi); } +static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf, + size_t len, unsigned int freq) +{ + const u8 *pos; + u8 *p2p; + int p2p_len; + int ret; + const struct ieee80211_mgmt *mgmt; + + mgmt = (const struct ieee80211_mgmt *) buf; + + /* Include P2P IE(s) from the frame generated in user space. */ + + p2p = kmalloc(len, GFP_KERNEL); + if (p2p == NULL) + return -ENOMEM; + p2p_len = 0; + + pos = mgmt->u.probe_resp.variable; + while (pos + 1 < buf + len) { + if (pos + 2 + pos[1] > buf + len) + break; + if (ath6kl_is_p2p_ie(pos)) { + memcpy(p2p + p2p_len, pos, 2 + pos[1]); + p2p_len += 2 + pos[1]; + } + pos += 2 + pos[1]; + } + + ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da, + p2p, p2p_len); + kfree(p2p); + return ret; +} + static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, @@ -1743,6 +1819,20 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, { struct ath6kl *ar = ath6kl_priv(dev); u32 id; + const struct ieee80211_mgmt *mgmt; + + mgmt = (const struct ieee80211_mgmt *) buf; + if (buf + len >= mgmt->u.probe_resp.variable && + ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* + * Send Probe Response frame in AP mode using a separate WMI + * command to allow the target to fill in the generic IEs. + */ + *cookie = 0; /* TX status not supported */ + return ath6kl_send_go_probe_resp(ar, buf, len, + chan->center_freq); + } id = ar->send_action_id++; if (id == 0) { From 1b1e6ee300b84eff3c7b0ee8de2396eb815f1b9d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:10 +0300 Subject: [PATCH 039/155] ath6kl: Return error from wmi.c instead of -EIO in ath6kl_cfg80211_scan Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 2136899561df..e867a7a5c91d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -759,12 +759,13 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return -EIO; if (!ar->usr_bss_filter) { - if (ath6kl_wmi_bssfilter_cmd(ar->wmi, - (test_bit(CONNECTED, &ar->flag) ? - ALL_BUT_BSS_FILTER : - ALL_BSS_FILTER), 0) != 0) { + ret = ath6kl_wmi_bssfilter_cmd( + ar->wmi, + (test_bit(CONNECTED, &ar->flag) ? + ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0); + if (ret) { ath6kl_err("couldn't set bss filtering\n"); - return -EIO; + return ret; } } @@ -807,11 +808,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, channels[i] = request->channels[i]->center_freq; } - if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, - false, 0, 0, n_channels, channels) != 0) { + ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, + false, 0, 0, n_channels, channels); + if (ret) ath6kl_err("wmi_startscan_cmd failed\n"); - ret = -EIO; - } ar->scan_req = request; From 4495ab167044d3ba3127dac06762138f5122ddc9 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 30 Aug 2011 21:58:11 +0300 Subject: [PATCH 040/155] ath6kl: Define __CHECK_ENDIAN__ for sparse Make sparse check endianness with "make C=1". Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index e1bb07ea8e80..b64a6f529834 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -33,3 +33,5 @@ ath6kl-y += txrx.o ath6kl-y += wmi.o ath6kl-y += node.o ath6kl-y += sdio.o + +ccflags-y += -D__CHECK_ENDIAN__ From 003353b0d27489228eff79447d0731687cea0207 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 1 Sep 2011 10:14:21 +0300 Subject: [PATCH 041/155] ath6kl: add testmode support This is port from the staging version of ath6kl. The interface to user space is exactly same. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/Makefile | 1 + drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 + drivers/net/wireless/ath/ath6kl/core.h | 8 + drivers/net/wireless/ath/ath6kl/init.c | 26 ++++ drivers/net/wireless/ath/ath6kl/main.c | 5 +- drivers/net/wireless/ath/ath6kl/testmode.c | 167 +++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/testmode.h | 20 +++ drivers/net/wireless/ath/ath6kl/wmi.c | 29 ++++ drivers/net/wireless/ath/ath6kl/wmi.h | 1 + 9 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 drivers/net/wireless/ath/ath6kl/testmode.c create mode 100644 drivers/net/wireless/ath/ath6kl/testmode.h diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index b64a6f529834..5fe092046d3e 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -33,5 +33,6 @@ ath6kl-y += txrx.o ath6kl-y += wmi.o ath6kl-y += node.o ath6kl-y += sdio.o +ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e867a7a5c91d..7db66589ee0c 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -18,6 +18,7 @@ #include "cfg80211.h" #include "debug.h" #include "hif-ops.h" +#include "testmode.h" #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ @@ -1907,6 +1908,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .set_pmksa = ath6kl_set_pmksa, .del_pmksa = ath6kl_del_pmksa, .flush_pmksa = ath6kl_flush_pmksa, + CFG80211_TESTMODE_CMD(ath6kl_tm_cmd) #ifdef CONFIG_PM .suspend = ar6k_cfg80211_suspend, #endif diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 60e2291fecc3..cfbbad9feb9e 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -64,6 +64,7 @@ #define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS 0x57e910 #define AR6003_REV2_OTP_FILE "ath6k/AR6003/hw2.0/otp.bin.z77" #define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77" +#define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin" #define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin" #define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" #define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin" @@ -72,6 +73,7 @@ #define AR6003_REV3_VERSION 0x30000582 #define AR6003_REV3_OTP_FILE "ath6k/AR6003/hw2.1.1/otp.bin" #define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin" +#define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin" #define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin" #define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" #define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \ @@ -358,6 +360,7 @@ struct ath6kl_req_key { #define NETDEV_REGISTERED 10 #define SKIP_SCAN 11 #define WLAN_ENABLED 12 +#define TESTMODE 13 struct ath6kl { struct device *dev; @@ -431,6 +434,11 @@ struct ath6kl { #define AR_MCAST_FILTER_MAC_ADDR_SIZE 4 u8 auto_auth_stage; + struct { + void *rx_report; + size_t rx_report_len; + } tm; + u16 conf_flags; wait_queue_head_t event_wq; struct ath6kl_mbox_info mbox_info; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 32b7ef5e2aca..f348357279a1 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -23,8 +23,10 @@ #include "hif-ops.h" unsigned int debug_mask; +static unsigned int testmode; module_param(debug_mask, uint, 0644); +module_param(testmode, uint, 0644); /* * Include definitions here that can be used to tune the WLAN module @@ -901,6 +903,28 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) u32 address; int ret; + if (testmode) { + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_TCMD_FIRMWARE_FILE; + break; + case AR6003_REV3_VERSION: + filename = AR6003_REV3_TCMD_FIRMWARE_FILE; + break; + case AR6004_REV1_VERSION: + ath6kl_warn("testmode not supported with ar6004\n"); + return -EOPNOTSUPP; + default: + ath6kl_warn("unknown target version: 0x%x\n", + ar->version.target_ver); + return -EINVAL; + } + + set_bit(TESTMODE, &ar->flag); + + goto get_fw; + } + switch (ar->version.target_ver) { case AR6003_REV2_VERSION: filename = AR6003_REV2_FIRMWARE_FILE; @@ -913,6 +937,8 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) break; } +get_fw: + if (ar->fw == NULL) { ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); if (ret) { diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 69a1b45179c5..0c4f39c6c44f 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -915,9 +915,10 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) set_bit(WMI_READY, &ar->flag); wake_up(&ar->event_wq); - ath6kl_info("hw %s fw %s\n", + ath6kl_info("hw %s fw %s%s\n", get_hw_id_string(ar->wdev->wiphy->hw_version), - ar->wdev->wiphy->fw_version); + ar->wdev->wiphy->fw_version, + test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); } void ath6kl_scan_complete_evt(struct ath6kl *ar, int status) diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c new file mode 100644 index 000000000000..381eb66a605f --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "testmode.h" + +#include + +enum ath6kl_tm_attr { + __ATH6KL_TM_ATTR_INVALID = 0, + ATH6KL_TM_ATTR_CMD = 1, + ATH6KL_TM_ATTR_DATA = 2, + + /* keep last */ + __ATH6KL_TM_ATTR_AFTER_LAST, + ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, +}; + +enum ath6kl_tm_cmd { + ATH6KL_TM_CMD_TCMD = 0, + ATH6KL_TM_CMD_RX_REPORT = 1, +}; + +#define ATH6KL_TM_DATA_MAX_LEN 5000 + +static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = { + [ATH6KL_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH6KL_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH6KL_TM_DATA_MAX_LEN }, +}; + +void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len) +{ + if (down_interruptible(&ar->sem)) + return; + + kfree(ar->tm.rx_report); + + ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL); + ar->tm.rx_report_len = buf_len; + + up(&ar->sem); + + wake_up(&ar->event_wq); +} + +static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len, + struct sk_buff *skb) +{ + int ret = 0; + long left; + + if (down_interruptible(&ar->sem)) + return -ERESTARTSYS; + + if (!test_bit(WMI_READY, &ar->flag)) { + ret = -EIO; + goto out; + } + + if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { + ret = -EBUSY; + goto out; + } + + if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) { + up(&ar->sem); + return -EIO; + } + + left = wait_event_interruptible_timeout(ar->event_wq, + ar->tm.rx_report != NULL, + WMI_TIMEOUT); + + if (left == 0) { + ret = -ETIMEDOUT; + goto out; + } else if (left < 0) { + ret = left; + goto out; + } + + if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) { + ret = -EINVAL; + goto out; + } + + NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len, + ar->tm.rx_report); + + kfree(ar->tm.rx_report); + ar->tm.rx_report = NULL; + +out: + up(&ar->sem); + + return ret; + +nla_put_failure: + ret = -ENOBUFS; + goto out; +} + +int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; + int err, buf_len, reply_len; + struct sk_buff *skb; + void *buf; + + err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, + ath6kl_tm_policy); + if (err) + return err; + + if (!tb[ATH6KL_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { + case ATH6KL_TM_CMD_TCMD: + if (!tb[ATH6KL_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); + + return 0; + + break; + case ATH6KL_TM_CMD_RX_REPORT: + if (!tb[ATH6KL_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); + + reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN); + skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len); + if (!skb) + return -ENOMEM; + + err = ath6kl_tm_rx_report(ar, buf, buf_len, skb); + if (err < 0) { + kfree_skb(skb); + return err; + } + + return cfg80211_testmode_reply(skb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h new file mode 100644 index 000000000000..2e6b723d1da2 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/testmode.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" + +void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len); +int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index dec869790c17..c34e36806dac 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -17,6 +17,7 @@ #include #include "core.h" #include "debug.h" +#include "testmode.h" static int ath6kl_wmi_sync_point(struct wmi *wmi); @@ -1136,6 +1137,13 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) return 0; } +static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len) +{ + ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len); + + return 0; +} + static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len) { if (len < sizeof(struct wmi_fix_rates_reply)) @@ -2509,6 +2517,23 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl) return ret; } +int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) +{ + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(len); + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, len); + + ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG); + + return ret; +} + + s32 ath6kl_wmi_get_rate(s8 rate_index) { if (rate_index == RATE_AUTO) @@ -3007,6 +3032,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) case WMI_REPORT_ROAM_DATA_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n"); break; + case WMI_TEST_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n"); + ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len); + break; case WMI_GET_FIXRATES_CMDID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n"); ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index cb3d27afcc65..5d68d8f2032c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2179,6 +2179,7 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi); int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg); int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl); +int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); From a846401f9f99f2b823a5d685b2977cc2f41134be Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 1 Sep 2011 12:04:59 +0300 Subject: [PATCH 042/155] ath6kl: fix compilation when NL80211_TESTMODE is disabled ERROR: "ath6kl_tm_rx_report_event" [drivers/net/wireless/ath/ath6kl/ath6kl.ko] undefined Reported-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/testmode.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h index 2e6b723d1da2..43dffcc11fb1 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.h +++ b/drivers/net/wireless/ath/ath6kl/testmode.h @@ -16,5 +16,21 @@ #include "core.h" +#ifdef CONFIG_NL80211_TESTMODE + void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len); int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); + +#else + +static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, + size_t buf_len) +{ +} + +static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +{ + return 0; +} + +#endif From d748753cd71f4504129fc6bd2262e0c5e4abe62f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 1 Sep 2011 11:33:20 +0300 Subject: [PATCH 043/155] ath6kl: Do not enable Probe Request reporting by default Probe Request reporting will be needed for P2P and WPS, but some firmware builds do not seem to like this when P2P is not enabled. Since we do not yet enable P2P, the safest option here is to just remove this call for now and bring it back as a more dynamic version once ath6kl starts advertising support for P2P. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index f348357279a1..96953be5cd73 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -473,13 +473,6 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar) ret); } - /* Enable Probe Request reporting for P2P */ - ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true); - if (ret) { - ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe Request " - "reporting (%d)\n", ret); - } - return status; } From bdf5396be177b689c00ae6ebed00d13fafaed36e Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Sep 2011 10:32:04 +0300 Subject: [PATCH 044/155] ath6kl: add firmware log support Firmware sends binary logs with WMIX_DBGLOG_EVENTID event. Create a buffer which stores the latest logs and which can be copied from fwlog debugfs file with cp command. To save memory firmware log support is enabled only when CONFIG_ATH6KL_DEBUG is enabled. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 9 ++ drivers/net/wireless/ath/ath6kl/debug.c | 154 ++++++++++++++++++++++- drivers/net/wireless/ath/ath6kl/debug.h | 13 ++ drivers/net/wireless/ath/ath6kl/init.c | 2 + drivers/net/wireless/ath/ath6kl/target.h | 3 + drivers/net/wireless/ath/ath6kl/wmi.c | 1 + 6 files changed, 181 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index cfbbad9feb9e..319e768d9ad6 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "htc.h" #include "wmi.h" @@ -467,6 +468,14 @@ struct ath6kl { u32 send_action_id; bool probe_req_report; u16 next_chan; + +#ifdef CONFIG_ATH6KL_DEBUG + struct { + struct circ_buf fwlog_buf; + spinlock_t fwlog_lock; + void *fwlog_tmp; + } debug; +#endif /* CONFIG_ATH6KL_DEBUG */ }; static inline void *ath6kl_priv(struct net_device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 2b462876cec1..b2706da58149 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -15,7 +15,23 @@ */ #include "core.h" + +#include + #include "debug.h" +#include "target.h" + +struct ath6kl_fwlog_slot { + __le32 timestamp; + __le32 length; + + /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ + u8 payload[0]; +}; + +#define ATH6KL_FWLOG_SIZE 32768 +#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \ + ATH6KL_FWLOG_PAYLOAD_SIZE) int ath6kl_printk(const char *level, const char *fmt, ...) { @@ -153,6 +169,117 @@ static int ath6kl_debugfs_open(struct inode *inode, struct file *file) return 0; } +static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf, + size_t buf_len) +{ + struct circ_buf *fwlog = &ar->debug.fwlog_buf; + size_t space; + int i; + + /* entries must all be equal size */ + if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE)) + return; + + space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE); + if (space < buf_len) + /* discard oldest slot */ + fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) & + (ATH6KL_FWLOG_SIZE - 1); + + for (i = 0; i < buf_len; i += space) { + space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail, + ATH6KL_FWLOG_SIZE); + + if ((size_t) space > buf_len - i) + space = buf_len - i; + + memcpy(&fwlog->buf[fwlog->head], buf, space); + fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1); + } + +} + +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) +{ + struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp; + size_t slot_len; + + if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) + return; + + spin_lock_bh(&ar->debug.fwlog_lock); + + slot->timestamp = cpu_to_le32(jiffies); + slot->length = cpu_to_le32(len); + memcpy(slot->payload, buf, len); + + slot_len = sizeof(*slot) + len; + + if (slot_len < ATH6KL_FWLOG_SLOT_SIZE) + memset(slot->payload + len, 0, + ATH6KL_FWLOG_SLOT_SIZE - slot_len); + + ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE); + + spin_unlock_bh(&ar->debug.fwlog_lock); +} + +static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar) +{ + return CIRC_CNT(ar->debug.fwlog_buf.head, + ar->debug.fwlog_buf.tail, + ATH6KL_FWLOG_SLOT_SIZE) == 0; +} + +static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + struct circ_buf *fwlog = &ar->debug.fwlog_buf; + size_t len = 0, buf_len = count; + ssize_t ret_cnt; + char *buf; + int ccnt; + + buf = vmalloc(buf_len); + if (!buf) + return -ENOMEM; + + spin_lock_bh(&ar->debug.fwlog_lock); + + while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) { + ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail, + ATH6KL_FWLOG_SIZE); + + if ((size_t) ccnt > buf_len - len) + ccnt = buf_len - len; + + memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt); + len += ccnt; + + fwlog->tail = (fwlog->tail + ccnt) & + (ATH6KL_FWLOG_SIZE - 1); + } + + spin_unlock_bh(&ar->debug.fwlog_lock); + + if (WARN_ON(len > buf_len)) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + vfree(buf); + + return ret_cnt; +} + +static const struct file_operations fops_fwlog = { + .open = ath6kl_debugfs_open, + .read = ath6kl_fwlog_read, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -358,10 +485,25 @@ static const struct file_operations fops_credit_dist_stats = { int ath6kl_debug_init(struct ath6kl *ar) { + ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); + if (ar->debug.fwlog_buf.buf == NULL) + return -ENOMEM; + + ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL); + if (ar->debug.fwlog_tmp == NULL) { + vfree(ar->debug.fwlog_buf.buf); + return -ENOMEM; + } + + spin_lock_init(&ar->debug.fwlog_lock); + ar->debugfs_phy = debugfs_create_dir("ath6kl", ar->wdev->wiphy->debugfsdir); - if (!ar->debugfs_phy) + if (!ar->debugfs_phy) { + vfree(ar->debug.fwlog_buf.buf); + kfree(ar->debug.fwlog_tmp); return -ENOMEM; + } debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_tgt_stats); @@ -369,6 +511,16 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, &fops_credit_dist_stats); + debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, + &fops_fwlog); + return 0; } + +void ath6kl_debug_cleanup(struct ath6kl *ar) +{ + vfree(ar->debug.fwlog_buf.buf); + kfree(ar->debug.fwlog_tmp); +} + #endif diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index e8c9ea9ce02c..f0d64711b410 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -78,7 +78,10 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_en_reg); void dump_cred_dist_stats(struct htc_target *target); +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len); int ath6kl_debug_init(struct ath6kl *ar); +void ath6kl_debug_cleanup(struct ath6kl *ar); + #else static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, const char *fmt, ...) @@ -101,9 +104,19 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev, static inline void dump_cred_dist_stats(struct htc_target *target) { } + +void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) +{ +} + static inline int ath6kl_debug_init(struct ath6kl *ar) { return 0; } + +void ath6kl_debug_cleanup(struct ath6kl *ar) +{ +} + #endif #endif diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 96953be5cd73..a638c3c9b79b 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1389,6 +1389,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister) ath6kl_bmi_cleanup(ar); + ath6kl_debug_cleanup(ar); + if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) { unregister_netdev(dev); clear_bit(NETDEV_REGISTERED, &ar->flag); diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 53e2c786f8e3..6c66a08e1793 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -340,4 +340,7 @@ struct host_interest { #define AR6004_REV1_BOARD_DATA_ADDRESS 0x435400 #define AR6004_REV1_BOARD_EXT_DATA_ADDRESS 0x437000 #define AR6004_REV1_RAM_RESERVE_SIZE 11264 + +#define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 + #endif diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index c34e36806dac..954d5e18e888 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2903,6 +2903,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) case WMIX_HB_CHALLENGE_RESP_EVENTID: break; case WMIX_DBGLOG_EVENTID: + ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len); break; default: ath6kl_err("unknown cmd id 0x%x\n", id); From 939f1ccec80bd2dad5638de2a6819c66d4cb6f32 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Sep 2011 10:32:04 +0300 Subject: [PATCH 045/155] ath6kl: implement support to set firmware log parameters Firmware log parameters can be controlled now with help of fwlog_mask debugfs file. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/debug.c | 51 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.c | 19 +++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 6 +++ 4 files changed, 77 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 319e768d9ad6..58c810acdbf2 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -474,6 +474,7 @@ struct ath6kl { struct circ_buf fwlog_buf; spinlock_t fwlog_lock; void *fwlog_tmp; + u32 fwlog_mask; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ }; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index b2706da58149..239c092d3e11 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -17,6 +17,7 @@ #include "core.h" #include +#include #include "debug.h" #include "target.h" @@ -32,6 +33,7 @@ struct ath6kl_fwlog_slot { #define ATH6KL_FWLOG_SIZE 32768 #define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \ ATH6KL_FWLOG_PAYLOAD_SIZE) +#define ATH6KL_FWLOG_VALID_MASK 0x1ffff int ath6kl_printk(const char *level, const char *fmt, ...) { @@ -280,6 +282,46 @@ static const struct file_operations fops_fwlog = { .llseek = default_llseek, }; +static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[16]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_fwlog_mask_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + int ret; + + ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask); + if (ret) + return ret; + + ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi, + ATH6KL_FWLOG_VALID_MASK, + ar->debug.fwlog_mask); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_fwlog_mask = { + .open = ath6kl_debugfs_open, + .read = ath6kl_fwlog_mask_read, + .write = ath6kl_fwlog_mask_write, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -497,6 +539,12 @@ int ath6kl_debug_init(struct ath6kl *ar) spin_lock_init(&ar->debug.fwlog_lock); + /* + * Actually we are lying here but don't know how to read the mask + * value from the firmware. + */ + ar->debug.fwlog_mask = 0; + ar->debugfs_phy = debugfs_create_dir("ath6kl", ar->wdev->wiphy->debugfsdir); if (!ar->debugfs_phy) { @@ -514,6 +562,9 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, &fops_fwlog); + debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, + ar, &fops_fwlog_mask); + return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 954d5e18e888..7201a72ac1b8 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2412,6 +2412,25 @@ int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source) return ret; } +int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config) +{ + struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data; + cmd->valid = cpu_to_le32(valid); + cmd->config = cpu_to_le32(config); + + ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + int ath6kl_wmi_get_stats_cmd(struct wmi *wmi) { return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 5d68d8f2032c..1c565816f622 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2083,6 +2083,11 @@ struct wmix_hb_challenge_resp_cmd { __le32 source; } __packed; +struct ath6kl_wmix_dbglog_cfg_module_cmd { + __le32 valid; + __le32 config; +} __packed; + /* End of Extended WMI (WMIX) */ enum wmi_sync_flag { @@ -2162,6 +2167,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status, u8 preamble_policy); int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); +int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config); int ath6kl_wmi_get_stats_cmd(struct wmi *wmi); int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index, From addb44be036dd5fc814be770ec4b90f08c820e76 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Sep 2011 10:32:05 +0300 Subject: [PATCH 046/155] ath6kl: cleanup diagnose window read and write functions Just to make them a bit easier to read and unify naming. 32 suffix in the function name means that it will be a 32 bit transfer. If there's no number a buffer is transfered instead. Use void pointers to get rid of ugly casts. Don't provide target address as a pointer, pass it by value. Same for the value used in write32(). Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 6 +- drivers/net/wireless/ath/ath6kl/init.c | 16 ++--- drivers/net/wireless/ath/ath6kl/main.c | 94 ++++++++++++++------------ 3 files changed, 61 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 58c810acdbf2..c5213d509093 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -507,9 +507,9 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); -int ath6kl_access_datadiag(struct ath6kl *ar, u32 address, - u8 *data, u32 length, bool read); -int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data); +int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); +int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); +int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); void ath6kl_init_profile_info(struct ath6kl *ar); void ath6kl_tx_data_cleanup(struct ath6kl *ar); void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile, diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index a638c3c9b79b..60baf448f548 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -133,14 +133,13 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar) address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)); address = TARG_VTOP(ar->target_type, address); - if (ath6kl_read_reg_diag(ar, &address, &data)) + if (ath6kl_diag_read32(ar, address, &data)) return -EIO; address = TARG_VTOP(ar->target_type, data); host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION; - if (ath6kl_access_datadiag(ar, address, - (u8 *)&host_app_area, - sizeof(struct host_app_area), false)) + if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area, + sizeof(struct host_app_area))) return -EIO; return 0; @@ -377,7 +376,7 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) address = TARG_VTOP(ar->target_type, address); /* read RAM location through diagnostic window */ - status = ath6kl_read_reg_diag(ar, &address, ®dump_loc); + status = ath6kl_diag_read32(ar, address, ®dump_loc); if (status || !regdump_loc) { ath6kl_err("failed to get ptr to register dump area\n"); @@ -389,11 +388,8 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar) regdump_loc = TARG_VTOP(ar->target_type, regdump_loc); /* fetch register dump data */ - status = ath6kl_access_datadiag(ar, - regdump_loc, - (u8 *)®dump_val[0], - REG_DUMP_COUNT_AR6003 * (sizeof(u32)), - true); + status = ath6kl_diag_read(ar, regdump_loc, (u8 *)®dump_val[0], + REG_DUMP_COUNT_AR6003 * (sizeof(u32))); if (status) { ath6kl_err("failed to get register dump\n"); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 0c4f39c6c44f..e346f835e779 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -229,74 +229,84 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) } /* - * Read from the ATH6KL through its diagnostic window. No cooperation from - * the Target is required for this. + * Read from the hardware through its diagnostic window. No cooperation + * from the firmware is required for this. */ -int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data) +int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value) { - int status; + int ret; /* set window register to start read cycle */ - status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, - *address); - - if (status) - return status; + ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address); + if (ret) + return ret; /* read the data */ - status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data, - sizeof(u32), HIF_RD_SYNC_BYTE_INC); - if (status) { - ath6kl_err("failed to read from window data addr\n"); - return status; + ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value, + sizeof(*value), HIF_RD_SYNC_BYTE_INC); + if (ret) { + ath6kl_warn("failed to read32 through diagnose window: %d\n", + ret); + return ret; } - return status; + return 0; } - /* * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ -static int ath6kl_write_reg_diag(struct ath6kl *ar, u32 *address, u32 *data) +static int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value) { - int status; + int ret; /* set write data */ - status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data, - sizeof(u32), HIF_WR_SYNC_BYTE_INC); - if (status) { - ath6kl_err("failed to write 0x%x to window data addr\n", *data); - return status; + ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value, + sizeof(value), HIF_WR_SYNC_BYTE_INC); + if (ret) { + ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n", + address, value); + return ret; } /* set window register, which starts the write cycle */ return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS, - *address); + address); } -int ath6kl_access_datadiag(struct ath6kl *ar, u32 address, - u8 *data, u32 length, bool read) +int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length) { - u32 count; - int status = 0; + u32 count, *buf = data; + int ret; - for (count = 0; count < length; count += 4, address += 4) { - if (read) { - status = ath6kl_read_reg_diag(ar, &address, - (u32 *) &data[count]); - if (status) - break; - } else { - status = ath6kl_write_reg_diag(ar, &address, - (u32 *) &data[count]); - if (status) - break; - } + if (WARN_ON(length % 4)) + return -EINVAL; + + for (count = 0; count < length / 4; count++, address += 4) { + ret = ath6kl_diag_read32(ar, address, &buf[count]); + if (ret) + return ret; } - return status; + return 0; +} + +int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length) +{ + u32 count, *buf = data; + int ret; + + if (WARN_ON(length % 4)) + return -EINVAL; + + for (count = 0; count < length / 4; count++, address += 4) { + ret = ath6kl_diag_write32(ar, address, buf[count]); + if (ret) + return ret; + } + + return 0; } /* FIXME: move to a better place, target.h? */ @@ -328,7 +338,7 @@ static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, break; } - status = ath6kl_write_reg_diag(ar, &address, &data); + status = ath6kl_diag_write32(ar, address, data); if (status) ath6kl_err("failed to reset target\n"); From bc07ddb29a7b71ad009bcd84bee4c93908cf22b6 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Sep 2011 10:32:05 +0300 Subject: [PATCH 047/155] ath6kl: read fwlog from firmware ring buffer Firmare sends the logs only when it's internal ring buffer is full. But if firmware crashes we need to retrieve the latest logs through diagnose window. This is now done everytime the debugfs file is read. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 15 +++++ drivers/net/wireless/ath/ath6kl/debug.c | 3 + drivers/net/wireless/ath/ath6kl/init.c | 13 ---- drivers/net/wireless/ath/ath6kl/main.c | 75 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/target.h | 14 +++++ 5 files changed, 107 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c5213d509093..65d0d84b4767 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -26,6 +26,7 @@ #include "htc.h" #include "wmi.h" #include "bmi.h" +#include "target.h" #define MAX_ATH6KL 1 #define ATH6KL_MAX_RX_BUFFERS 16 @@ -494,6 +495,19 @@ static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info cred_info->cur_free_credits -= credits; } +static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar, + u32 item_offset) +{ + u32 addr = 0; + + if (ar->target_type == TARGET_TYPE_AR6003) + addr = ATH6KL_AR6003_HI_START_ADDR + item_offset; + else if (ar->target_type == TARGET_TYPE_AR6004) + addr = ATH6KL_AR6004_HI_START_ADDR + item_offset; + + return addr; +} + void ath6kl_destroy(struct net_device *dev, unsigned int unregister); int ath6kl_configure_target(struct ath6kl *ar); void ath6kl_detect_error(unsigned long ptr); @@ -510,6 +524,7 @@ void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); +int ath6kl_read_fwlogs(struct ath6kl *ar); void ath6kl_init_profile_info(struct ath6kl *ar); void ath6kl_tx_data_cleanup(struct ath6kl *ar); void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile, diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 239c092d3e11..87de44d0ee33 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -247,6 +247,9 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; + /* read undelivered logs from firmware */ + ath6kl_read_fwlogs(ar); + spin_lock_bh(&ar->debug.fwlog_lock); while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 60baf448f548..d234dc22e709 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -110,19 +110,6 @@ static u8 ath6kl_get_fw_iftype(struct ath6kl *ar) } } -static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar, - u32 item_offset) -{ - u32 addr = 0; - - if (ar->target_type == TARGET_TYPE_AR6003) - addr = ATH6KL_AR6003_HI_START_ADDR + item_offset; - else if (ar->target_type == TARGET_TYPE_AR6004) - addr = ATH6KL_AR6004_HI_START_ADDR + item_offset; - - return addr; -} - static int ath6kl_set_host_app_area(struct ath6kl *ar) { u32 address, data; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index e346f835e779..937c7a238c12 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -309,6 +309,81 @@ int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length) return 0; } +int ath6kl_read_fwlogs(struct ath6kl *ar) +{ + struct ath6kl_dbglog_hdr debug_hdr; + struct ath6kl_dbglog_buf debug_buf; + u32 address, length, dropped, firstbuf, debug_hdr_addr; + int ret = 0, loop; + u8 *buf; + + buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + address = TARG_VTOP(ar->target_type, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_dbglog_hdr))); + + ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr); + if (ret) + goto out; + + /* Get the contents of the ring buffer */ + if (debug_hdr_addr == 0) { + ath6kl_warn("Invalid address for debug_hdr_addr\n"); + ret = -EINVAL; + goto out; + } + + address = TARG_VTOP(ar->target_type, debug_hdr_addr); + ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_hdr.dbuf_addr)); + firstbuf = address; + dropped = le32_to_cpu(debug_hdr.dropped); + ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + + loop = 100; + + do { + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_buf.buffer_addr)); + length = le32_to_cpu(debug_buf.length); + + if (length != 0 && (le32_to_cpu(debug_buf.length) <= + le32_to_cpu(debug_buf.bufsize))) { + length = ALIGN(length, 4); + + ret = ath6kl_diag_read(ar, address, + buf, length); + if (ret) + goto out; + + ath6kl_debug_fwlog_event(ar, buf, length); + } + + address = TARG_VTOP(ar->target_type, + le32_to_cpu(debug_buf.next)); + ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + if (ret) + goto out; + + loop--; + + if (WARN_ON(loop == 0)) { + ret = -ETIMEDOUT; + goto out; + } + } while (address != firstbuf); + +out: + kfree(buf); + + return ret; +} + /* FIXME: move to a better place, target.h? */ #define AR6003_RESET_CONTROL_ADDRESS 0x00004000 #define AR6004_RESET_CONTROL_ADDRESS 0x00004000 diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 6c66a08e1793..dd8b953cbfc0 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -343,4 +343,18 @@ struct host_interest { #define ATH6KL_FWLOG_PAYLOAD_SIZE 1500 +struct ath6kl_dbglog_buf { + __le32 next; + __le32 buffer_addr; + __le32 bufsize; + __le32 length; + __le32 count; + __le32 free; +} __packed; + +struct ath6kl_dbglog_hdr { + __le32 dbuf_addr; + __le32 dropped; +} __packed; + #endif From 91d57de5adfc40184ef7cb8974104459c5211add Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 2 Sep 2011 10:40:06 +0300 Subject: [PATCH 048/155] ath6kl: Add debugfs interface to dump diagnostic registers from firmware To dump a particular register: echo > /ieee80211/phyX/ath6kl/reg_addr To dump the entire register set: echo 0 > /ieee80211/phyX/ath6kl/reg_addr Register values will be available at: cat /ieee80211/phyX/ath6kl/reg_dump kvalo: commit log cleanup, renamed few functions, removed a warning message Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/debug.c | 192 ++++++++++++++++++++++++ 2 files changed, 193 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 65d0d84b4767..de5308727b62 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -476,6 +476,7 @@ struct ath6kl { spinlock_t fwlog_lock; void *fwlog_tmp; u32 fwlog_mask; + unsigned int dbgfs_diag_reg; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ }; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 87de44d0ee33..cb89776f9485 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -54,6 +54,27 @@ int ath6kl_printk(const char *level, const char *fmt, ...) } #ifdef CONFIG_ATH6KL_DEBUG + +#define REG_OUTPUT_LEN_PER_LINE 25 +#define REGTYPE_STR_LEN 100 + +struct ath6kl_diag_reg_info { + u32 reg_start; + u32 reg_end; + const char *reg_info; +}; + +static const struct ath6kl_diag_reg_info diag_reg[] = { + { 0x20000, 0x200fc, "General DMA and Rx registers" }, + { 0x28000, 0x28900, "MAC PCU register & keycache" }, + { 0x20800, 0x20a40, "QCU" }, + { 0x21000, 0x212f0, "DCU" }, + { 0x4000, 0x42e4, "RTC" }, + { 0x540000, 0x540000 + (256 * 1024), "RAM" }, + { 0x29800, 0x2B210, "Base Band" }, + { 0x1C000, 0x1C748, "Analog" }, +}; + void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_enable_reg) @@ -528,6 +549,171 @@ static const struct file_operations fops_credit_dist_stats = { .llseek = default_llseek, }; +static unsigned long ath6kl_get_num_reg(void) +{ + int i; + unsigned long n_reg = 0; + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) + n_reg = n_reg + + (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1; + + return n_reg; +} + +static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { + if (reg_addr >= diag_reg[i].reg_start && + reg_addr <= diag_reg[i].reg_end) + return true; + } + + return false; +} + +static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[50]; + unsigned int len = 0; + + if (ar->debug.dbgfs_diag_reg) + len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", + ar->debug.dbgfs_diag_reg); + else + len += scnprintf(buf + len, sizeof(buf) - len, + "All diag registers\n"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_regread_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[50]; + unsigned int len; + unsigned long reg_addr; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (strict_strtoul(buf, 0, ®_addr)) + return -EINVAL; + + if ((reg_addr % 4) != 0) + return -EINVAL; + + if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr)) + return -EINVAL; + + ar->debug.dbgfs_diag_reg = reg_addr; + + return count; +} + +static const struct file_operations fops_diag_reg_read = { + .read = ath6kl_regread_read, + .write = ath6kl_regread_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath6kl_regdump_open(struct inode *inode, struct file *file) +{ + struct ath6kl *ar = inode->i_private; + u8 *buf; + unsigned long int reg_len; + unsigned int len = 0, n_reg; + u32 addr; + __le32 reg_val; + int i, status; + + /* Dump all the registers if no register is specified */ + if (!ar->debug.dbgfs_diag_reg) + n_reg = ath6kl_get_num_reg(); + else + n_reg = 1; + + reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE; + if (n_reg > 1) + reg_len += REGTYPE_STR_LEN; + + buf = vmalloc(reg_len); + if (!buf) + return -ENOMEM; + + if (n_reg == 1) { + addr = ar->debug.dbgfs_diag_reg; + + status = ath6kl_diag_read32(ar, + TARG_VTOP(ar->target_type, addr), + (u32 *)®_val); + if (status) + goto fail_reg_read; + + len += scnprintf(buf + len, reg_len - len, + "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val)); + goto done; + } + + for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { + len += scnprintf(buf + len, reg_len - len, + "%s\n", diag_reg[i].reg_info); + for (addr = diag_reg[i].reg_start; + addr <= diag_reg[i].reg_end; addr += 4) { + status = ath6kl_diag_read32(ar, + TARG_VTOP(ar->target_type, addr), + (u32 *)®_val); + if (status) + goto fail_reg_read; + + len += scnprintf(buf + len, reg_len - len, + "0x%06x 0x%08x\n", + addr, le32_to_cpu(reg_val)); + } + } + +done: + file->private_data = buf; + return 0; + +fail_reg_read: + ath6kl_warn("Unable to read memory:%u\n", addr); + vfree(buf); + return -EIO; +} + +static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + u8 *buf = file->private_data; + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static int ath6kl_regdump_release(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static const struct file_operations fops_reg_dump = { + .open = ath6kl_regdump_open, + .read = ath6kl_regdump_read, + .release = ath6kl_regdump_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath6kl_debug_init(struct ath6kl *ar) { ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); @@ -568,6 +754,12 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_fwlog_mask); + debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, + &fops_diag_reg_read); + + debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar, + &fops_reg_dump); + return 0; } From b142b91401b8e39671db74bd4fe89f281f4c2978 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 31 Aug 2011 15:48:15 +0530 Subject: [PATCH 049/155] ath6kl: Fix endianness in requesting chip register read Need to make sure the chip address for which we need the value si endian safe. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 937c7a238c12..bda14d1d6f2f 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -178,8 +178,8 @@ void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie) static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) { int status; - u8 addr_val[4]; s32 i; + __le32 addr_val; /* * Write bytes 1,2,3 of the register to set the upper address bytes, @@ -189,16 +189,18 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) for (i = 1; i <= 3; i++) { /* * Fill the buffer with the address byte value we want to - * hit 4 times. + * hit 4 times. No need to worry about endianness as the + * same byte is copied to all four bytes of addr_val at + * any time. */ - memset(addr_val, ((u8 *)&addr)[i], 4); + memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4); /* * Hit each byte of the register address with a 4-byte * write operation to the same address, this is a harmless * operation. */ - status = hif_read_write_sync(ar, reg_addr + i, addr_val, + status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val, 4, HIF_WR_SYNC_BYTE_FIX); if (status) break; @@ -216,7 +218,9 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) * cycle to start, the extra 3 byte write to bytes 1,2,3 has no * effect since we are writing the same values again */ - status = hif_read_write_sync(ar, reg_addr, (u8 *)(&addr), + addr_val = cpu_to_le32(addr); + status = hif_read_write_sync(ar, reg_addr, + (u8 *)&(addr_val), 4, HIF_WR_SYNC_BYTE_INC); if (status) { From e5090444be811ce45653969363be8fcb4c52d597 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Wed, 31 Aug 2011 15:02:19 +0530 Subject: [PATCH 050/155] ath6kl: Add debugfs entry to modify roaming parameters. Firmware initiates roaming only after it reaches a rssi of 20. This lower rssi threshold can be modified through a wmi command to modify the roaming behavior. kvalo: rename debugfs functions and move comment about rssi units next to ath6kl_wmi_set_roam_lrssi_cmd() Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/debug.c | 47 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/init.c | 1 + drivers/net/wireless/ath/ath6kl/wmi.c | 29 +++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 41 +++++++++++++++++++++ 5 files changed, 119 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index de5308727b62..faf801571214 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -394,6 +394,7 @@ struct ath6kl { u16 bss_ch; u16 listen_intvl_b; u16 listen_intvl_t; + u8 lrssi_roam_threshold; struct ath6kl_version version; u32 target_type; u8 tx_pwr; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index cb89776f9485..8bc475376372 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -714,6 +714,51 @@ static const struct file_operations fops_reg_dump = { .llseek = default_llseek, }; +static ssize_t ath6kl_lrssi_roam_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + unsigned long lrssi_roam_threshold; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &lrssi_roam_threshold)) + return -EINVAL; + + ar->lrssi_roam_threshold = lrssi_roam_threshold; + + ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold); + + return count; +} + +static ssize_t ath6kl_lrssi_roam_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + unsigned int len; + + len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_lrssi_roam_threshold = { + .read = ath6kl_lrssi_roam_read, + .write = ath6kl_lrssi_roam_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath6kl_debug_init(struct ath6kl *ar) { ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); @@ -760,6 +805,8 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar, &fops_reg_dump); + debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_lrssi_roam_threshold); return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index d234dc22e709..3d67025b72c4 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -280,6 +280,7 @@ static void ath6kl_init_control_info(struct ath6kl *ar) memset(&ar->sc_params, 0, sizeof(ar->sc_params)); ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT; ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS; + ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; memset((u8 *)ar->sta_list, 0, AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 7201a72ac1b8..c9ec6303db72 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -666,6 +666,35 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len) return 0; } +/* + * Mechanism to modify the roaming behavior in the firmware. The lower rssi + * at which the station has to roam can be passed with + * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level + * in dBm. + */ +int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi) +{ + struct sk_buff *skb; + struct roam_ctrl_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct roam_ctrl_cmd *) skb->data; + + cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD); + cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi + + DEF_SCAN_FOR_ROAM_INTVL); + cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi); + cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR; + cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS; + + ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG); + + return 0; +} + static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_connect_event *ev; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 1c565816f622..a78e21b91776 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1333,6 +1333,46 @@ enum wmi_bi_ftype { PROBEREQ_FTYPE, }; +#define DEF_LRSSI_SCAN_PERIOD 5 +#define DEF_LRSSI_ROAM_THRESHOLD 20 +#define DEF_LRSSI_ROAM_FLOOR 60 +#define DEF_SCAN_FOR_ROAM_INTVL 2 + +enum wmi_roam_ctrl { + WMI_FORCE_ROAM = 1, + WMI_SET_ROAM_MODE, + WMI_SET_HOST_BIAS, + WMI_SET_LRSSI_SCAN_PARAMS, +}; + +struct bss_bias { + u8 bssid[ETH_ALEN]; + u8 bias; +} __packed; + +struct bss_bias_info { + u8 num_bss; + struct bss_bias bss_bias[1]; +} __packed; + +struct low_rssi_scan_params { + __le16 lrssi_scan_period; + a_sle16 lrssi_scan_threshold; + a_sle16 lrssi_roam_threshold; + u8 roam_rssi_floor; + u8 reserved[1]; +} __packed; + +struct roam_ctrl_cmd { + union { + u8 bssid[ETH_ALEN]; + u8 roam_mode; + struct bss_bias_info bss; + struct low_rssi_scan_params params; + } __packed info; + u8 roam_ctrl; +} __packed; + struct wmi_bss_info_hdr { __le16 ch; @@ -2190,6 +2230,7 @@ int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd); +int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid, u32 ssid_len, bool is_wpa2, From 38c35ffd38be9fbbf2ec0b67a802472d3f58f9fa Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 5 Sep 2011 11:19:45 +0300 Subject: [PATCH 051/155] ath6kl: Make ath6kl_diag_write32() non-static So that this can be called from debug.c when adding support to write chip register. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/main.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index faf801571214..e69cd5b552a7 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -523,6 +523,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value); int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index bda14d1d6f2f..48e9c2e0eae8 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -261,7 +261,7 @@ int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value) * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ -static int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value) +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value) { int ret; From f9ea0753a18448a5e92369317b6ac061fe1275bf Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 5 Sep 2011 11:19:46 +0300 Subject: [PATCH 052/155] ath6kl: Fix endianness in register write Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 2 +- drivers/net/wireless/ath/ath6kl/main.c | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index e69cd5b552a7..ae2f59137622 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -523,7 +523,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar); -int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value); +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value); int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length); int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value); int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 48e9c2e0eae8..3cefca65fc0c 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -261,7 +261,7 @@ int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value) * Write to the ATH6KL through its diagnostic window. No cooperation from * the Target is required for this. */ -int ath6kl_diag_write32(struct ath6kl *ar, u32 address, u32 value) +int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value) { int ret; @@ -298,7 +298,8 @@ int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length) int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length) { - u32 count, *buf = data; + u32 count; + __le32 *buf = data; int ret; if (WARN_ON(length % 4)) @@ -397,13 +398,14 @@ static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type, { int status = 0; u32 address; - u32 data; + __le32 data; if (target_type != TARGET_TYPE_AR6003 && target_type != TARGET_TYPE_AR6004) return; - data = cold_reset ? RESET_CONTROL_COLD_RST : RESET_CONTROL_MBOX_RST; + data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) : + cpu_to_le32(RESET_CONTROL_MBOX_RST); switch (target_type) { case TARGET_TYPE_AR6003: From 252c068b9fba57493940af344b6d92ee3c278941 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 5 Sep 2011 11:19:46 +0300 Subject: [PATCH 053/155] ath6kl: Add debugfs support to write a chip register To write a value to register: echo = > /ieee80211/phyX/ath6kl/reg_write kvalo: rename file to reg_write to follow the style of other debugfs files Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 2 + drivers/net/wireless/ath/ath6kl/debug.c | 66 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index ae2f59137622..fdb796fe79b2 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -478,6 +478,8 @@ struct ath6kl { void *fwlog_tmp; u32 fwlog_mask; unsigned int dbgfs_diag_reg; + u32 diag_reg_addr_wr; + u32 diag_reg_val_wr; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ }; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 8bc475376372..4fc83ccbf8bd 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -759,6 +759,68 @@ static const struct file_operations fops_lrssi_roam_threshold = { .llseek = default_llseek, }; +static ssize_t ath6kl_regwrite_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + u8 buf[32]; + unsigned int len = 0; + + len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n", + ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath6kl_regwrite_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char buf[32]; + char *sptr, *token; + unsigned int len = 0; + u32 reg_addr, reg_val; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, "="); + if (!token) + return -EINVAL; + + if (kstrtou32(token, 0, ®_addr)) + return -EINVAL; + + if (!ath6kl_dbg_is_diag_reg_valid(reg_addr)) + return -EINVAL; + + if (kstrtou32(sptr, 0, ®_val)) + return -EINVAL; + + ar->debug.diag_reg_addr_wr = reg_addr; + ar->debug.diag_reg_val_wr = reg_val; + + if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr, + cpu_to_le32(ar->debug.diag_reg_val_wr))) + return -EIO; + + return count; +} + +static const struct file_operations fops_diag_reg_write = { + .read = ath6kl_regwrite_read, + .write = ath6kl_regwrite_write, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath6kl_debug_init(struct ath6kl *ar) { ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); @@ -807,6 +869,10 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_lrssi_roam_threshold); + + debugfs_create_file("reg_write", S_IRUSR | S_IWUSR, + ar->debugfs_phy, ar, &fops_diag_reg_write); + return 0; } From 11869befc7285be712623536daa30791aec1682f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 2 Sep 2011 20:07:06 +0300 Subject: [PATCH 054/155] athk6l: Fix channel list processing in scan requests Limit the length of the channel list to WMI_MAX_CHANNELS to avoid rejection of the request in wmi.c. Since there is not really much point in using a specific list of more than 32 channels, drop the channel list if more channels are specified and scan all channels. Fix cfg80211 scan API use: ar->scan_req must be set only if returning success from scan() handler. The previous version would result in use of freed memory and likely kernel panic should the scan request fail to be sent to the target. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 7db66589ee0c..1fe55f6f5d17 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -793,10 +793,16 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } } - if (request->n_channels > 0) { + /* + * Scan only the requested channels if the request specifies a set of + * channels. If the list is longer than the target supports, do not + * configure the list and instead, scan all available channels. + */ + if (request->n_channels > 0 && + request->n_channels <= WMI_MAX_CHANNELS) { u8 i; - n_channels = min(127U, request->n_channels); + n_channels = request->n_channels; channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); if (channels == NULL) { @@ -813,8 +819,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, false, 0, 0, n_channels, channels); if (ret) ath6kl_err("wmi_startscan_cmd failed\n"); - - ar->scan_req = request; + else + ar->scan_req = request; kfree(channels); From d6e51e6a0cc50b6dd8d9f3a733427cca3f9afdee Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 5 Sep 2011 17:38:44 +0300 Subject: [PATCH 055/155] ath6kl: Fix WMI message structure for AP_SET_PVB There is a 2-octet reserved field between the flag and aid fields. Fix that to make the target actually behave as requested. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 1 + drivers/net/wireless/ath/ath6kl/wmi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index c9ec6303db72..b2c5c40727fb 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2740,6 +2740,7 @@ int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag) cmd = (struct wmi_ap_set_pvb_cmd *) skb->data; cmd->aid = cpu_to_le16(aid); + cmd->rsvd = cpu_to_le16(0); cmd->flag = cpu_to_le32(flag); ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index a78e21b91776..e86b81d326eb 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1922,6 +1922,7 @@ struct wmi_ap_set_mlme_cmd { struct wmi_ap_set_pvb_cmd { __le32 flag; + __le16 rsvd; __le16 aid; } __packed; From 572e27c00c9d1250ae2b4951eae7e73992174138 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 5 Sep 2011 17:38:45 +0300 Subject: [PATCH 056/155] ath6kl: Fix AP mode connect event parsing and TIM updates This cleans up the connect event parsing by defining a union in struct wmi_connect_event to match with the three possible sets of fields that the target uses depending on which type of connect event is being indicated. In addition, two AP cases are now separated from ath6kl_connect_event() so that correct field names can be used to make it actually possible to understand what the code is doing. The bug hiding in the previous mess was in parsing the AID incorrectly when processing the new station connecting event in AP mode. The fix here for that is also fixing TIM updates for PS buffering to use the correct AID. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 4 ++ drivers/net/wireless/ath/ath6kl/main.c | 92 +++++++++++--------------- drivers/net/wireless/ath/ath6kl/wmi.c | 44 ++++++++++-- drivers/net/wireless/ath/ath6kl/wmi.h | 29 ++++++-- 4 files changed, 107 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index fdb796fe79b2..054da13ce488 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -560,6 +560,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info); +void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel); +void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr, + u8 keymgmt, u8 ucipher, u8 auth, + u8 assoc_req_len, u8 *assoc_info); void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 prot_reason_status); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 3cefca65fc0c..d510046c99d6 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -519,57 +519,55 @@ static void ath6kl_install_static_wep_keys(struct ath6kl *ar) } } -static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, - u16 listen_int, u16 beacon_int, - u8 assoc_req_len, u8 *assoc_info) +void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel) { - struct net_device *dev = ar->net_dev; - u8 *ies = NULL, *wpa_ie = NULL, *pos; - size_t ies_len = 0; - struct station_info sinfo; struct ath6kl_req_key *ik; int res; u8 key_rsc[ATH6KL_KEY_SEQ_LEN]; - if (memcmp(dev->dev_addr, bssid, ETH_ALEN) == 0) { - ik = &ar->ap_mode_bkey; + ik = &ar->ap_mode_bkey; - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", - channel); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel); - switch (ar->auth_mode) { - case NONE_AUTH: - if (ar->prwise_crypto == WEP_CRYPT) - ath6kl_install_static_wep_keys(ar); + switch (ar->auth_mode) { + case NONE_AUTH: + if (ar->prwise_crypto == WEP_CRYPT) + ath6kl_install_static_wep_keys(ar); + break; + case WPA_PSK_AUTH: + case WPA2_PSK_AUTH: + case (WPA_PSK_AUTH | WPA2_PSK_AUTH): + if (!ik->valid) break; - case WPA_PSK_AUTH: - case WPA2_PSK_AUTH: - case (WPA_PSK_AUTH|WPA2_PSK_AUTH): - if (!ik->valid) - break; - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for " - "the initial group key for AP mode\n"); - memset(key_rsc, 0, sizeof(key_rsc)); - res = ath6kl_wmi_addkey_cmd( - ar->wmi, ik->key_index, ik->key_type, - GROUP_USAGE, ik->key_len, key_rsc, ik->key, - KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); - if (res) { - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed " - "addkey failed: %d\n", res); - } - break; + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for " + "the initial group key for AP mode\n"); + memset(key_rsc, 0, sizeof(key_rsc)); + res = ath6kl_wmi_addkey_cmd( + ar->wmi, ik->key_index, ik->key_type, + GROUP_USAGE, ik->key_len, key_rsc, ik->key, + KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); + if (res) { + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed " + "addkey failed: %d\n", res); } - - ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); - set_bit(CONNECTED, &ar->flag); - netif_carrier_on(ar->net_dev); - return; + break; } - ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", - bssid, channel); + ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); + set_bit(CONNECTED, &ar->flag); + netif_carrier_on(ar->net_dev); +} + +void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr, + u8 keymgmt, u8 ucipher, u8 auth, + u8 assoc_req_len, u8 *assoc_info) +{ + u8 *ies = NULL, *wpa_ie = NULL, *pos; + size_t ies_len = 0; + struct station_info sinfo; + + ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid); if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) { struct ieee80211_mgmt *mgmt = @@ -606,10 +604,9 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, pos += 2 + pos[1]; } - ath6kl_add_new_sta(ar, bssid, channel, wpa_ie, + ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie, wpa_ie ? 2 + wpa_ie[1] : 0, - listen_int & 0xFF, beacon_int, - (listen_int >> 8) & 0xFF); + keymgmt, ucipher, auth); /* send event to application */ memset(&sinfo, 0, sizeof(sinfo)); @@ -620,11 +617,9 @@ static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid, sinfo.assoc_req_ies_len = ies_len; sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; - cfg80211_new_sta(ar->net_dev, bssid, &sinfo, GFP_KERNEL); + cfg80211_new_sta(ar->net_dev, mac_addr, &sinfo, GFP_KERNEL); netif_wake_queue(ar->net_dev); - - return; } /* Functions for Tx credit handling */ @@ -1030,13 +1025,6 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, { unsigned long flags; - if (ar->nw_type == AP_NETWORK) { - ath6kl_connect_ap_mode(ar, channel, bssid, listen_int, - beacon_int, assoc_req_len, - assoc_info + beacon_ie_len); - return; - } - ath6kl_cfg80211_connect_event(ar, channel, bssid, listen_int, beacon_int, net_type, beacon_ie_len, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index b2c5c40727fb..b56830f6d474 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -699,14 +699,47 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_connect_event *ev; u8 *pie, *peie; + struct ath6kl *ar = wmi->parent_dev; if (len < sizeof(struct wmi_connect_event)) return -EINVAL; ev = (struct wmi_connect_event *) datap; + if (ar->nw_type == AP_NETWORK) { + /* AP mode start/STA connected event */ + struct net_device *dev = ar->net_dev; + if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) { + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM " + "(AP started)\n", + __func__, le16_to_cpu(ev->u.ap_bss.ch), + ev->u.ap_bss.bssid); + ath6kl_connect_ap_mode_bss( + ar, le16_to_cpu(ev->u.ap_bss.ch)); + } else { + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM " + "auth=%u keymgmt=%u cipher=%u apsd_info=%u " + "(STA connected)\n", + __func__, ev->u.ap_sta.aid, + ev->u.ap_sta.mac_addr, + ev->u.ap_sta.auth, + ev->u.ap_sta.keymgmt, + le16_to_cpu(ev->u.ap_sta.cipher), + ev->u.ap_sta.apsd_info); + ath6kl_connect_ap_mode_sta( + ar, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, + ev->u.ap_sta.keymgmt, + le16_to_cpu(ev->u.ap_sta.cipher), + ev->u.ap_sta.auth, ev->assoc_req_len, + ev->assoc_info + ev->beacon_ie_len); + } + return 0; + } + + /* STA/IBSS mode connection event */ + ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM\n", - __func__, ev->ch, ev->bssid); + __func__, le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid); /* Start of assoc rsp IEs */ pie = ev->assoc_info + ev->beacon_ie_len + @@ -735,10 +768,11 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) pie += pie[1] + 2; } - ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->ch), ev->bssid, - le16_to_cpu(ev->listen_intvl), - le16_to_cpu(ev->beacon_intvl), - le32_to_cpu(ev->nw_type), + ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->u.sta.ch), + ev->u.sta.bssid, + le16_to_cpu(ev->u.sta.listen_intvl), + le16_to_cpu(ev->u.sta.beacon_intvl), + le32_to_cpu(ev->u.sta.nw_type), ev->beacon_ie_len, ev->assoc_req_len, ev->assoc_resp_len, ev->assoc_info); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index e86b81d326eb..5ca8c8e904cf 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1275,11 +1275,30 @@ struct wmi_ready_event_2 { /* Connect Event */ struct wmi_connect_event { - __le16 ch; - u8 bssid[ETH_ALEN]; - __le16 listen_intvl; - __le16 beacon_intvl; - __le32 nw_type; + union { + struct { + __le16 ch; + u8 bssid[ETH_ALEN]; + __le16 listen_intvl; + __le16 beacon_intvl; + __le32 nw_type; + } sta; + struct { + u8 phymode; + u8 aid; + u8 mac_addr[ETH_ALEN]; + u8 auth; + u8 keymgmt; + __le16 cipher; + u8 apsd_info; + u8 unused[3]; + } ap_sta; + struct { + __le16 ch; + u8 bssid[ETH_ALEN]; + u8 unused[8]; + } ap_bss; + } u; u8 beacon_ie_len; u8 assoc_req_len; u8 assoc_resp_len; From 6e4604c8b91743c5a797fc2674544618854ed0f2 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 5 Sep 2011 17:38:46 +0300 Subject: [PATCH 057/155] ath6kl: Allow AP mode to be configured Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 1fe55f6f5d17..640569688219 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1221,6 +1221,9 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, case NL80211_IFTYPE_ADHOC: ar->next_mode = ADHOC_NETWORK; break; + case NL80211_IFTYPE_AP: + ar->next_mode = AP_NETWORK; + break; case NL80211_IFTYPE_P2P_CLIENT: ar->next_mode = INFRA_NETWORK; break; @@ -1956,7 +1959,7 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) set_wiphy_dev(wdev->wiphy, dev); wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); /* max num of ssids that can be probed during scanning */ wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ From 060337604577e55c5bf3246bcaf161929c603d54 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Tue, 6 Sep 2011 13:01:36 +0530 Subject: [PATCH 058/155] ath6kl: Process regulatory requests from firmware. Process the regulatory code from eeprom and pass the country information to cfg80211. kvalo: add space between struct name and * Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 78 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 6 +++ 2 files changed, 84 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index b56830f6d474..dbddb91389d0 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -18,6 +18,8 @@ #include "core.h" #include "debug.h" #include "testmode.h" +#include "../regd.h" +#include "../regd_common.h" static int ath6kl_wmi_sync_point(struct wmi *wmi); @@ -779,6 +781,81 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) return 0; } +static struct country_code_to_enum_rd * +ath6kl_regd_find_country(u16 countryCode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (allCountries[i].countryCode == countryCode) + return &allCountries[i]; + } + + return NULL; +} + +static struct reg_dmn_pair_mapping * +ath6kl_get_regpair(u16 regdmn) +{ + int i; + + if (regdmn == NO_ENUMRD) + return NULL; + + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { + if (regDomainPairs[i].regDmnEnum == regdmn) + return ®DomainPairs[i]; + } + + return NULL; +} + +static struct country_code_to_enum_rd * +ath6kl_regd_find_country_by_rd(u16 regdmn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (allCountries[i].regDmnEnum == regdmn) + return &allCountries[i]; + } + + return NULL; +} + +static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) +{ + + struct ath6kl_wmi_regdomain *ev; + struct country_code_to_enum_rd *country = NULL; + struct reg_dmn_pair_mapping *regpair = NULL; + char alpha2[2]; + u32 reg_code; + + ev = (struct ath6kl_wmi_regdomain *) datap; + reg_code = le32_to_cpu(ev->reg_code); + + if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) + country = ath6kl_regd_find_country((u16) reg_code); + else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) { + + regpair = ath6kl_get_regpair((u16) reg_code); + country = ath6kl_regd_find_country_by_rd((u16) reg_code); + ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl: Regpair used: 0x%0x\n", + regpair->regDmnEnum); + } + + if (country) { + alpha2[0] = country->isoName[0]; + alpha2[1] = country->isoName[1]; + + regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2); + + ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl: Country alpha2 being used: %c%c\n", + alpha2[0], alpha2[1]); + } +} + static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len) { struct wmi_disconnect_event *ev; @@ -3068,6 +3145,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_REGDOMAIN_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); + ath6kl_wmi_regdomain_event(wmi, datap, len); break; case WMI_PSTREAM_TIMEOUT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n"); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 5ca8c8e904cf..dc49ef86c1c8 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1325,6 +1325,12 @@ enum wmi_disconnect_reason { IBSS_MERGE = 0xe, }; +#define ATH6KL_COUNTRY_RD_SHIFT 16 + +struct ath6kl_wmi_regdomain { + __le32 reg_code; +}; + struct wmi_disconnect_event { /* reason code, see 802.11 spec. */ __le16 proto_reason_status; From dfa0104c2a2699e73a49b4ca10bbb99796b05889 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 6 Sep 2011 11:10:49 +0300 Subject: [PATCH 059/155] ath6kl: unify tx function names in htc.c This is to make it easier follow tx code path inside htc.No functional changes. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 87 ++++++++++++++------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index 77e548ce2c03..46c6efbcd31b 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -22,7 +22,7 @@ #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) -static void ath6kl_htc_buf_align(u8 **buf, unsigned long len) +static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len) { u8 *align_addr; @@ -33,8 +33,8 @@ static void ath6kl_htc_buf_align(u8 **buf, unsigned long len) } } -static void htc_prep_send_pkt(struct htc_packet *packet, u8 flags, int ctrl0, - int ctrl1) +static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags, + int ctrl0, int ctrl1) { struct htc_frame_hdr *hdr; @@ -178,7 +178,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target, htc_tx_complete(endpoint, &tx_compq); } -static int htc_issue_send(struct htc_target *target, struct htc_packet *packet) +static int ath6kl_htc_tx_issue(struct htc_target *target, + struct htc_packet *packet) { int status; bool sync = false; @@ -276,9 +277,9 @@ static int htc_check_credits(struct htc_target *target, return 0; } -static void htc_tx_pkts_get(struct htc_target *target, - struct htc_endpoint *endpoint, - struct list_head *queue) +static void ath6kl_htc_tx_pkts_get(struct htc_target *target, + struct htc_endpoint *endpoint, + struct list_head *queue) { int req_cred; u8 flags; @@ -357,11 +358,11 @@ static int htc_get_credit_padding(unsigned int cred_sz, int *len, return cred_pad; } -static int htc_setup_send_scat_list(struct htc_target *target, - struct htc_endpoint *endpoint, - struct hif_scatter_req *scat_req, - int n_scat, - struct list_head *queue) +static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, + struct htc_endpoint *endpoint, + struct hif_scatter_req *scat_req, + int n_scat, + struct list_head *queue) { struct htc_packet *packet; int i, len, rem_scat, cred_pad; @@ -393,12 +394,12 @@ static int htc_setup_send_scat_list(struct htc_target *target, scat_req->scat_list[i].packet = packet; /* prepare packet and flag message as part of a send bundle */ - htc_prep_send_pkt(packet, + ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE, cred_pad, packet->info.tx.seqno); /* Make sure the buffer is 4-byte aligned */ - ath6kl_htc_buf_align(&packet->buf, - packet->act_len + HTC_HDR_LENGTH); + ath6kl_htc_tx_buf_align(&packet->buf, + packet->act_len + HTC_HDR_LENGTH); scat_req->scat_list[i].buf = packet->buf; scat_req->scat_list[i].len = len; @@ -425,18 +426,17 @@ static int htc_setup_send_scat_list(struct htc_target *target, } /* - * htc_issue_send_bundle: drain a queue and send as bundles - * this function may return without fully draining the queue - * when + * Drain a queue and send as bundles this function may return without fully + * draining the queue when * * 1. scatter resources are exhausted * 2. a message that will consume a partial credit will stop the * bundling process early * 3. we drop below the minimum number of messages for a bundle */ -static void htc_issue_send_bundle(struct htc_endpoint *endpoint, - struct list_head *queue, - int *sent_bundle, int *n_bundle_pkts) +static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, + struct list_head *queue, + int *sent_bundle, int *n_bundle_pkts) { struct htc_target *target = endpoint->target; struct hif_scatter_req *scat_req = NULL; @@ -467,8 +467,9 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, scat_req->len = 0; scat_req->scat_entries = 0; - status = htc_setup_send_scat_list(target, endpoint, - scat_req, n_scat, queue); + status = ath6kl_htc_tx_setup_scat_list(target, endpoint, + scat_req, n_scat, + queue); if (status == -EAGAIN) { hif_scatter_req_add(target->dev->ar, scat_req); break; @@ -490,14 +491,14 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint, *sent_bundle = n_sent_bundle; *n_bundle_pkts = tot_pkts_bundle; - ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_issue_send_bundle (sent:%d)\n", - n_sent_bundle); + ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s (sent:%d)\n", + __func__, n_sent_bundle); return; } -static void htc_tx_from_ep_txq(struct htc_target *target, - struct htc_endpoint *endpoint) +static void ath6kl_htc_tx_from_queue(struct htc_target *target, + struct htc_endpoint *endpoint) { struct list_head txq; struct htc_packet *packet; @@ -525,7 +526,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target, if (list_empty(&endpoint->txq)) break; - htc_tx_pkts_get(target, endpoint, &txq); + ath6kl_htc_tx_pkts_get(target, endpoint, &txq); if (list_empty(&txq)) break; @@ -542,8 +543,8 @@ static void htc_tx_from_ep_txq(struct htc_target *target, HTC_MIN_HTC_MSGS_TO_BUNDLE)) { int temp1 = 0, temp2 = 0; - htc_issue_send_bundle(endpoint, &txq, - &temp1, &temp2); + ath6kl_htc_tx_bundle(endpoint, &txq, + &temp1, &temp2); bundle_sent += temp1; n_pkts_bundle += temp2; } @@ -555,9 +556,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target, list); list_del(&packet->list); - htc_prep_send_pkt(packet, packet->info.tx.flags, - 0, packet->info.tx.seqno); - htc_issue_send(target, packet); + ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, + 0, packet->info.tx.seqno); + ath6kl_htc_tx_issue(target, packet); } spin_lock_bh(&target->tx_lock); @@ -570,9 +571,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target, spin_unlock_bh(&target->tx_lock); } -static bool htc_try_send(struct htc_target *target, - struct htc_endpoint *endpoint, - struct htc_packet *tx_pkt) +static bool ath6kl_htc_tx_try(struct htc_target *target, + struct htc_endpoint *endpoint, + struct htc_packet *tx_pkt) { struct htc_ep_callbacks ep_cb; int txq_depth; @@ -608,7 +609,7 @@ static bool htc_try_send(struct htc_target *target, list_add_tail(&tx_pkt->list, &endpoint->txq); spin_unlock_bh(&target->tx_lock); - htc_tx_from_ep_txq(target, endpoint); + ath6kl_htc_tx_from_queue(target, endpoint); return true; } @@ -642,7 +643,7 @@ static void htc_chk_ep_txq(struct htc_target *target) * chance to reclaim credits from lower priority * ones. */ - htc_tx_from_ep_txq(target, endpoint); + ath6kl_htc_tx_from_queue(target, endpoint); spin_lock_bh(&target->tx_lock); } spin_unlock_bh(&target->tx_lock); @@ -694,8 +695,8 @@ static int htc_setup_tx_complete(struct htc_target *target) /* we want synchronous operation */ send_pkt->completion = NULL; - htc_prep_send_pkt(send_pkt, 0, 0, 0); - status = htc_issue_send(target, send_pkt); + ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); + status = ath6kl_htc_tx_issue(target, send_pkt); if (send_pkt != NULL) htc_reclaim_txctrl_buf(target, send_pkt); @@ -747,7 +748,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) endpoint = &target->endpoint[packet->endpoint]; - if (!htc_try_send(target, endpoint, packet)) { + if (!ath6kl_htc_tx_try(target, endpoint, packet)) { packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ? -ECANCELED : -ENOSPC; INIT_LIST_HEAD(&queue); @@ -2048,8 +2049,8 @@ int ath6kl_htc_conn_service(struct htc_target *target, /* we want synchronous operation */ tx_pkt->completion = NULL; - htc_prep_send_pkt(tx_pkt, 0, 0, 0); - status = htc_issue_send(target, tx_pkt); + ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0); + status = ath6kl_htc_tx_issue(target, tx_pkt); if (status) goto fail_tx; From 689def90ac16df09e2b6ca6af86dca68706cc75b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 6 Sep 2011 11:10:49 +0300 Subject: [PATCH 060/155] ath6kl: unify rx function naming in htc.c Similarly like with the tx path, unify naming in htc rx path. No functional changes. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 126 ++++++++++++++------------ 1 file changed, 66 insertions(+), 60 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index 46c6efbcd31b..9aa2e4447900 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -861,8 +861,8 @@ void ath6kl_htc_indicate_activity_change(struct htc_target *target, /* HTC Rx */ -static inline void htc_update_rx_stats(struct htc_endpoint *endpoint, - int n_look_ahds) +static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint, + int n_look_ahds) { endpoint->ep_st.rx_pkts++; if (n_look_ahds == 1) @@ -909,8 +909,9 @@ static void reclaim_rx_ctrl_buf(struct htc_target *target, spin_unlock_bh(&target->htc_lock); } -static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet, - u32 rx_len) +static int ath6kl_htc_rx_packet(struct htc_target *target, + struct htc_packet *packet, + u32 rx_len) { struct ath6kl_device *dev = target->dev; u32 padded_len; @@ -944,9 +945,9 @@ static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet, * "hint" that there are more single-packets to fetch * on this endpoint. */ -static void set_rxpkt_indication_flag(u32 lk_ahd, - struct htc_endpoint *endpoint, - struct htc_packet *packet) +static void ath6kl_htc_rx_set_indicate(u32 lk_ahd, + struct htc_endpoint *endpoint, + struct htc_packet *packet) { struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd; @@ -957,7 +958,7 @@ static void set_rxpkt_indication_flag(u32 lk_ahd, } } -static void chk_rx_water_mark(struct htc_endpoint *endpoint) +static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint) { struct htc_ep_callbacks ep_cb = endpoint->ep_cb; @@ -974,8 +975,9 @@ static void chk_rx_water_mark(struct htc_endpoint *endpoint) } /* This function is called with rx_lock held */ -static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep, - u32 *lk_ahds, struct list_head *queue, int n_msg) +static int ath6kl_htc_rx_setup(struct htc_target *target, + struct htc_endpoint *ep, + u32 *lk_ahds, struct list_head *queue, int n_msg) { struct htc_packet *packet; /* FIXME: type of lk_ahds can't be right */ @@ -1075,10 +1077,10 @@ static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep, return status; } -static int alloc_and_prep_rxpkts(struct htc_target *target, - u32 lk_ahds[], int msg, - struct htc_endpoint *endpoint, - struct list_head *queue) +static int ath6kl_htc_rx_alloc(struct htc_target *target, + u32 lk_ahds[], int msg, + struct htc_endpoint *endpoint, + struct list_head *queue) { int status = 0; struct htc_packet *packet, *tmp_pkt; @@ -1144,8 +1146,8 @@ static int alloc_and_prep_rxpkts(struct htc_target *target, n_msg = 1; /* Setup packet buffers for each message */ - status = htc_setup_rxpkts(target, endpoint, &lk_ahds[i], queue, - n_msg); + status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i], + queue, n_msg); /* * This is due to unavailabilty of buffers to rx entire data. @@ -1422,9 +1424,9 @@ static int htc_proc_trailer(struct htc_target *target, return status; } -static int htc_proc_rxhdr(struct htc_target *target, - struct htc_packet *packet, - u32 *next_lkahds, int *n_lkahds) +static int ath6kl_htc_rx_process_hdr(struct htc_target *target, + struct htc_packet *packet, + u32 *next_lkahds, int *n_lkahds) { int status = 0; u16 payload_len; @@ -1476,8 +1478,8 @@ static int htc_proc_rxhdr(struct htc_target *target, } if (lk_ahd != packet->info.rx.exp_hdr) { - ath6kl_err("htc_proc_rxhdr, lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", - packet, packet->info.rx.rx_flags); + ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", + __func__, packet, packet->info.rx.rx_flags); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd", &packet->info.rx.exp_hdr, 4); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header", @@ -1489,8 +1491,8 @@ static int htc_proc_rxhdr(struct htc_target *target, if (htc_hdr->flags & HTC_FLG_RX_TRAILER) { if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) || htc_hdr->ctrl[0] > payload_len) { - ath6kl_err("htc_proc_rxhdr, invalid hdr (payload len should be :%d, CB[0] is:%d)\n", - payload_len, htc_hdr->ctrl[0]); + ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n", + __func__, payload_len, htc_hdr->ctrl[0]); status = -ENOMEM; goto fail_rx; } @@ -1529,8 +1531,8 @@ fail_rx: return status; } -static void do_rx_completion(struct htc_endpoint *endpoint, - struct htc_packet *packet) +static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint, + struct htc_packet *packet) { ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "htc calling ep %d recv callback on packet 0x%p\n", @@ -1538,10 +1540,10 @@ static void do_rx_completion(struct htc_endpoint *endpoint, endpoint->ep_cb.rx(endpoint->target, packet); } -static int htc_issue_rxpkt_bundle(struct htc_target *target, - struct list_head *rxq, - struct list_head *sync_compq, - int *n_pkt_fetched, bool part_bundle) +static int ath6kl_htc_rx_bundle(struct htc_target *target, + struct list_head *rxq, + struct list_head *sync_compq, + int *n_pkt_fetched, bool part_bundle) { struct hif_scatter_req *scat_req; struct htc_packet *packet; @@ -1563,15 +1565,15 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target, * This would only happen if the target ignored our max * bundle limit. */ - ath6kl_warn("htc_issue_rxpkt_bundle : partial bundle detected num:%d , %d\n", - get_queue_depth(rxq), n_scat_pkt); + ath6kl_warn("%s(): partial bundle detected num:%d , %d\n", + __func__, get_queue_depth(rxq), n_scat_pkt); } len = 0; ath6kl_dbg(ATH6KL_DBG_HTC_RECV, - "htc_issue_rxpkt_bundle (numpackets: %d , actual : %d)\n", - get_queue_depth(rxq), n_scat_pkt); + "%s(): (numpackets: %d , actual : %d)\n", + __func__, get_queue_depth(rxq), n_scat_pkt); scat_req = hif_scatter_req_get(target->dev->ar); @@ -1631,9 +1633,10 @@ fail_rx_pkt: return status; } -static int htc_proc_fetched_rxpkts(struct htc_target *target, - struct list_head *comp_pktq, u32 lk_ahds[], - int *n_lk_ahd) +static int ath6kl_htc_rx_process_packets(struct htc_target *target, + struct list_head *comp_pktq, + u32 lk_ahds[], + int *n_lk_ahd) { struct htc_packet *packet, *tmp_pkt; struct htc_endpoint *ep; @@ -1644,7 +1647,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, ep = &target->endpoint[packet->endpoint]; /* process header for each of the recv packet */ - status = htc_proc_rxhdr(target, packet, lk_ahds, n_lk_ahd); + status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds, + n_lk_ahd); if (status) return status; @@ -1654,8 +1658,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, * based on the lookahead. */ if (*n_lk_ahd > 0) - set_rxpkt_indication_flag(lk_ahds[0], - ep, packet); + ath6kl_htc_rx_set_indicate(lk_ahds[0], + ep, packet); } else /* * Packets in a bundle automatically have @@ -1664,20 +1668,20 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target, packet->info.rx.indicat_flags |= HTC_RX_FLAGS_INDICATE_MORE_PKTS; - htc_update_rx_stats(ep, *n_lk_ahd); + ath6kl_htc_rx_update_stats(ep, *n_lk_ahd); if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE) ep->ep_st.rx_bundl += 1; - do_rx_completion(ep, packet); + ath6kl_htc_rx_complete(ep, packet); } return status; } -static int htc_fetch_rxpkts(struct htc_target *target, - struct list_head *rx_pktq, - struct list_head *comp_pktq) +static int ath6kl_htc_rx_fetch(struct htc_target *target, + struct list_head *rx_pktq, + struct list_head *comp_pktq) { int fetched_pkts; bool part_bundle = false; @@ -1693,10 +1697,10 @@ static int htc_fetch_rxpkts(struct htc_target *target, * bundle transfer and recv bundling is * allowed. */ - status = htc_issue_rxpkt_bundle(target, rx_pktq, - comp_pktq, - &fetched_pkts, - part_bundle); + status = ath6kl_htc_rx_bundle(target, rx_pktq, + comp_pktq, + &fetched_pkts, + part_bundle); if (status) return status; @@ -1725,7 +1729,8 @@ static int htc_fetch_rxpkts(struct htc_target *target, HTC_RX_PKT_IGNORE_LOOKAHEAD; /* go fetch the packet */ - status = dev_rx_pkt(target, packet, packet->act_len); + status = ath6kl_htc_rx_packet(target, packet, + packet->act_len); if (status) return status; @@ -1779,9 +1784,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, * Try to allocate as many HTC RX packets indicated by the * look_aheads. */ - status = alloc_and_prep_rxpkts(target, look_aheads, - num_look_ahead, endpoint, - &rx_pktq); + status = ath6kl_htc_rx_alloc(target, look_aheads, + num_look_ahead, endpoint, + &rx_pktq); if (status) break; @@ -1796,14 +1801,15 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, num_look_ahead = 0; - status = htc_fetch_rxpkts(target, &rx_pktq, &comp_pktq); + status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq); if (!status) - chk_rx_water_mark(endpoint); + ath6kl_htc_rx_chk_water_mark(endpoint); /* Process fetched packets */ - status = htc_proc_fetched_rxpkts(target, &comp_pktq, - look_aheads, &num_look_ahead); + status = ath6kl_htc_rx_process_packets(target, &comp_pktq, + look_aheads, + &num_look_ahead); if (!num_look_ahead || status) break; @@ -1896,14 +1902,14 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) packet->completion = NULL; /* get the message from the device, this will block */ - if (dev_rx_pkt(target, packet, packet->act_len)) + if (ath6kl_htc_rx_packet(target, packet, packet->act_len)) goto fail_ctrl_rx; /* process receive header */ - packet->status = htc_proc_rxhdr(target, packet, NULL, NULL); + packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL); if (packet->status) { - ath6kl_err("htc_wait_for_ctrl_msg, htc_proc_rxhdr failed (status = %d)\n", + ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n", packet->status); goto fail_ctrl_rx; } @@ -1950,7 +1956,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { packet->status = -ECANCELED; list_del(&packet->list); - do_rx_completion(endpoint, packet); + ath6kl_htc_rx_complete(endpoint, packet); } return status; From 6bbc7c35ed0fb61c7739e91d5ee7016455770511 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 5 Sep 2011 17:38:47 +0300 Subject: [PATCH 061/155] ath6kl: Allow enabling of P2P support For now, use a module parameter (ath6kl_p2p) to allow P2P support to be enabled. This is needed since there is no mechanism for enabling the P2P mode more dynamically for a single netdev. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 12 +++++++++ drivers/net/wireless/ath/ath6kl/core.h | 2 ++ drivers/net/wireless/ath/ath6kl/init.c | 31 +++++++++++++++++----- drivers/net/wireless/ath/ath6kl/target.h | 6 +++++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 640569688219..a889bf4a4722 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -20,6 +20,10 @@ #include "hif-ops.h" #include "testmode.h" +static unsigned int ath6kl_p2p; + +module_param(ath6kl_p2p, uint, 0644); + #define RATETAB_ENT(_rate, _rateid, _flags) { \ .bitrate = (_rate), \ .flags = (_flags), \ @@ -1936,6 +1940,7 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) { int ret = 0; struct wireless_dev *wdev; + struct ath6kl *ar; wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!wdev) { @@ -1951,6 +1956,9 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) return NULL; } + ar = wiphy_priv(wdev->wiphy); + ar->p2p = !!ath6kl_p2p; + wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes; wdev->wiphy->max_remain_on_channel_duration = 5000; @@ -1960,6 +1968,10 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev) wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + if (ar->p2p) { + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT); + } /* max num of ssids that can be probed during scanning */ wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 054da13ce488..c6ed1fc42bd9 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -471,6 +471,8 @@ struct ath6kl { bool probe_req_report; u16 next_chan; + bool p2p; + #ifdef CONFIG_ATH6KL_DEBUG struct { struct circ_buf fwlog_buf; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 3d67025b72c4..eca34aa6e4ba 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -448,13 +448,26 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar) status = -EIO; } - ret = ath6kl_wmi_info_req_cmd(ar->wmi, P2P_FLAG_CAPABILITIES_REQ | - P2P_FLAG_MACADDR_REQ | - P2P_FLAG_HMODEL_REQ); - if (ret) { - ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " - "capabilities (%d) - assuming P2P not supported\n", - ret); + if (ar->p2p) { + ret = ath6kl_wmi_info_req_cmd(ar->wmi, + P2P_FLAG_CAPABILITIES_REQ | + P2P_FLAG_MACADDR_REQ | + P2P_FLAG_HMODEL_REQ); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " + "capabilities (%d) - assuming P2P not " + "supported\n", ret); + ar->p2p = 0; + } + } + + if (ar->p2p) { + /* Enable Probe Request reporting for P2P */ + ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true); + if (ret) { + ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe " + "Request reporting (%d)\n", ret); + } } return status; @@ -492,6 +505,10 @@ int ath6kl_configure_target(struct ath6kl *ar) param |= (1 << HI_OPTION_NUM_DEV_SHIFT); param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT); + if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) { + param |= HI_OPTION_FW_SUBMODE_P2PDEV << + HI_OPTION_FW_SUBMODE_SHIFT; + } param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index dd8b953cbfc0..7db06a5d9194 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -304,6 +304,11 @@ struct host_interest { #define HI_OPTION_FW_MODE_BSS_STA 0x1 #define HI_OPTION_FW_MODE_AP 0x2 +#define HI_OPTION_FW_SUBMODE_NONE 0x0 +#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 +#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 +#define HI_OPTION_FW_SUBMODE_P2PGO 0x3 + #define HI_OPTION_NUM_DEV_SHIFT 0x9 #define HI_OPTION_FW_BRIDGE_SHIFT 0x04 @@ -316,6 +321,7 @@ struct host_interest { |------------------------------------------------------------------------------| */ #define HI_OPTION_FW_MODE_SHIFT 0xC +#define HI_OPTION_FW_SUBMODE_SHIFT 0x14 /* Convert a Target virtual address into a Target physical address */ #define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff) From 92ecbff48e3993ca58525533dc58ec1025c45609 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 7 Sep 2011 10:55:16 +0300 Subject: [PATCH 062/155] ath6kl: query device tree for firmware board-id When no default board data file is present query the device tree for a board-id setting to identify the board data to use. If the FDT lacks the necesary info fall back to the previous behaviour of using a compile-time board filename. Signed-off-by: Sam Leffler Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index eca34aa6e4ba..91716709cac8 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -15,6 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include "core.h" #include "cfg80211.h" @@ -680,6 +681,64 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, return ret; } +#ifdef CONFIG_OF +static const char *get_target_ver_dir(const struct ath6kl *ar) +{ + switch (ar->version.target_ver) { + case AR6003_REV1_VERSION: + return "ath6k/AR6003/hw1.0"; + case AR6003_REV2_VERSION: + return "ath6k/AR6003/hw2.0"; + case AR6003_REV3_VERSION: + return "ath6k/AR6003/hw2.1.1"; + } + ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, + ar->version.target_ver); + return NULL; +} + +/* + * Check the device tree for a board-id and use it to construct + * the pathname to the firmware file. Used (for now) to find a + * fallback to the "bdata.bin" file--typically a symlink to the + * appropriate board-specific file. + */ +static bool check_device_tree(struct ath6kl *ar) +{ + static const char *board_id_prop = "atheros,board-id"; + struct device_node *node; + char board_filename[64]; + const char *board_id; + int ret; + + for_each_compatible_node(node, NULL, "atheros,ath6kl") { + board_id = of_get_property(node, board_id_prop, NULL); + if (board_id == NULL) { + ath6kl_warn("No \"%s\" property on %s node.\n", + board_id_prop, node->name); + continue; + } + snprintf(board_filename, sizeof(board_filename), + "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id); + + ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board, + &ar->fw_board_len); + if (ret) { + ath6kl_err("Failed to get DT board file %s: %d\n", + board_filename, ret); + continue; + } + return true; + } + return false; +} +#else +static bool check_device_tree(struct ath6kl *ar) +{ + return false; +} +#endif /* CONFIG_OF */ + static int ath6kl_fetch_board_file(struct ath6kl *ar) { const char *filename; @@ -704,6 +763,11 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) return 0; } + if (check_device_tree(ar)) { + /* got board file from device tree */ + return 0; + } + /* there was no proper board file, try to use default instead */ ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", filename, ret); From 772c31ee438e4d2d7a5e049b8d73c2ee8902f656 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:16 +0300 Subject: [PATCH 063/155] ath6kl: separate firmware fetch from upload In preparation for the new firmware image format. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 249 +++++++++++++++---------- 1 file changed, 153 insertions(+), 96 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 91716709cac8..4055947ffd67 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -744,6 +744,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) const char *filename; int ret; + if (ar->fw_board != NULL) + return 0; + switch (ar->version.target_ver) { case AR6003_REV2_VERSION: filename = AR6003_REV2_BOARD_DATA_FILE; @@ -798,6 +801,144 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) return 0; } +static int ath6kl_fetch_otp_file(struct ath6kl *ar) +{ + const char *filename; + int ret; + + if (ar->fw_otp != NULL) + return 0; + + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_OTP_FILE; + break; + case AR6004_REV1_VERSION: + ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); + return 0; + break; + default: + filename = AR6003_REV3_OTP_FILE; + break; + } + + ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, + &ar->fw_otp_len); + if (ret) { + ath6kl_err("Failed to get OTP file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_fw_file(struct ath6kl *ar) +{ + const char *filename; + int ret; + + if (ar->fw != NULL) + return 0; + + if (testmode) { + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_TCMD_FIRMWARE_FILE; + break; + case AR6003_REV3_VERSION: + filename = AR6003_REV3_TCMD_FIRMWARE_FILE; + break; + case AR6004_REV1_VERSION: + ath6kl_warn("testmode not supported with ar6004\n"); + return -EOPNOTSUPP; + default: + ath6kl_warn("unknown target version: 0x%x\n", + ar->version.target_ver); + return -EINVAL; + } + + set_bit(TESTMODE, &ar->flag); + + goto get_fw; + } + + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_FIRMWARE_FILE; + break; + case AR6004_REV1_VERSION: + filename = AR6004_REV1_FIRMWARE_FILE; + break; + default: + filename = AR6003_REV3_FIRMWARE_FILE; + break; + } + +get_fw: + ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); + if (ret) { + ath6kl_err("Failed to get firmware file %s: %d\n", + filename, ret); + return ret; + } + + return 0; +} + +static int ath6kl_fetch_patch_file(struct ath6kl *ar) +{ + const char *filename; + int ret; + + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_PATCH_FILE; + break; + case AR6004_REV1_VERSION: + /* FIXME: implement for AR6004 */ + return 0; + break; + default: + filename = AR6003_REV3_PATCH_FILE; + break; + } + + if (ar->fw_patch == NULL) { + ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, + &ar->fw_patch_len); + if (ret) { + ath6kl_err("Failed to get patch file %s: %d\n", + filename, ret); + return ret; + } + } + + return 0; +} + +static int ath6kl_fetch_firmwares(struct ath6kl *ar) +{ + int ret; + + ret = ath6kl_fetch_board_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_otp_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_fw_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_patch_file(ar); + if (ret) + return ret; + + return 0; +} static int ath6kl_upload_board_file(struct ath6kl *ar) { @@ -805,11 +946,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) u32 board_data_size, board_ext_data_size; int ret; - if (ar->fw_board == NULL) { - ret = ath6kl_fetch_board_file(ar); - if (ret) - return ret; - } + if (WARN_ON(ar->fw_board == NULL)) + return -ENOENT; /* * Determine where in Target RAM to write Board Data. @@ -909,32 +1047,11 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) static int ath6kl_upload_otp(struct ath6kl *ar) { - const char *filename; u32 address, param; int ret; - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_OTP_FILE; - break; - case AR6004_REV1_VERSION: - ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); - return 0; - break; - default: - filename = AR6003_REV3_OTP_FILE; - break; - } - - if (ar->fw_otp == NULL) { - ret = ath6kl_get_fw(ar, filename, &ar->fw_otp, - &ar->fw_otp_len); - if (ret) { - ath6kl_err("Failed to get OTP file %s: %d\n", - filename, ret); - return ret; - } - } + if (WARN_ON(ar->fw_otp == NULL)) + return -ENOENT; address = ath6kl_get_load_address(ar->version.target_ver, APP_LOAD_ADDR); @@ -957,54 +1074,11 @@ static int ath6kl_upload_otp(struct ath6kl *ar) static int ath6kl_upload_firmware(struct ath6kl *ar) { - const char *filename; u32 address; int ret; - if (testmode) { - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_TCMD_FIRMWARE_FILE; - break; - case AR6003_REV3_VERSION: - filename = AR6003_REV3_TCMD_FIRMWARE_FILE; - break; - case AR6004_REV1_VERSION: - ath6kl_warn("testmode not supported with ar6004\n"); - return -EOPNOTSUPP; - default: - ath6kl_warn("unknown target version: 0x%x\n", - ar->version.target_ver); - return -EINVAL; - } - - set_bit(TESTMODE, &ar->flag); - - goto get_fw; - } - - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_FIRMWARE_FILE; - break; - case AR6004_REV1_VERSION: - filename = AR6004_REV1_FIRMWARE_FILE; - break; - default: - filename = AR6003_REV3_FIRMWARE_FILE; - break; - } - -get_fw: - - if (ar->fw == NULL) { - ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); - if (ret) { - ath6kl_err("Failed to get firmware file %s: %d\n", - filename, ret); - return ret; - } - } + if (WARN_ON(ar->fw == NULL)) + return -ENOENT; address = ath6kl_get_load_address(ar->version.target_ver, APP_LOAD_ADDR); @@ -1030,32 +1104,11 @@ get_fw: static int ath6kl_upload_patch(struct ath6kl *ar) { - const char *filename; u32 address, param; int ret; - switch (ar->version.target_ver) { - case AR6003_REV2_VERSION: - filename = AR6003_REV2_PATCH_FILE; - break; - case AR6004_REV1_VERSION: - /* FIXME: implement for AR6004 */ - return 0; - break; - default: - filename = AR6003_REV3_PATCH_FILE; - break; - } - - if (ar->fw_patch == NULL) { - ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, - &ar->fw_patch_len); - if (ret) { - ath6kl_err("Failed to get patch file %s: %d\n", - filename, ret); - return ret; - } - } + if (WARN_ON(ar->fw_patch == NULL)) + return -ENOENT; address = ath6kl_get_load_address(ar->version.target_ver, DATASET_PATCH_ADDR); @@ -1362,6 +1415,10 @@ int ath6kl_core_init(struct ath6kl *ar) goto err_htc_cleanup; } + ret = ath6kl_fetch_firmwares(ar); + if (ret) + goto err_htc_cleanup; + ret = ath6kl_init_upload(ar); if (ret) goto err_htc_cleanup; From cfc301edfb4f762309b5704ae316ea98d7ba1106 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:16 +0300 Subject: [PATCH 064/155] ath6kl: fix busy loop in ath6kl_bmi_get_rx_lkahd() Brent reported that ath6kl busy loops if firmware doesn't boot for some reason (in this case he was using an older firmware which wasn't supported by ath6kl). Investigation revealed that this was even on purpose, ath6kl_bmi_get_rx_lkahd() had a parameter to disable the timeout check, which is extremely evil. I didn't find any reason why the timeout needs to be disabled so I just removed the feature. The function already busyloops a maximum of one second if it doesn't get an answer, even that's too long. If something takes longer than that a more friendly approach is needed. Reported-by: Brent Taylor Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/bmi.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index 84676697d7eb..c5d11cc536e0 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -62,14 +62,14 @@ static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar) return 0; } -static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar, bool need_timeout) +static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar) { unsigned long timeout; u32 rx_word = 0; int ret = 0; timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); - while ((!need_timeout || time_before(jiffies, timeout)) && !rx_word) { + while (time_before(jiffies, timeout) && !rx_word) { ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, (u8 *)&rx_word, sizeof(rx_word), HIF_RD_SYNC_BYTE_INC); @@ -109,8 +109,7 @@ static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len) return ret; } -static int ath6kl_bmi_recv_buf(struct ath6kl *ar, - u8 *buf, u32 len, bool want_timeout) +static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len) { int ret; u32 addr; @@ -162,7 +161,7 @@ static int ath6kl_bmi_recv_buf(struct ath6kl *ar, * a function of Host processor speed. */ if (len >= 4) { /* NB: Currently, always true */ - ret = ath6kl_bmi_get_rx_lkahd(ar, want_timeout); + ret = ath6kl_bmi_get_rx_lkahd(ar); if (ret) return ret; } @@ -220,7 +219,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, } ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version, - sizeof(targ_info->version), true); + sizeof(targ_info->version)); if (ret) { ath6kl_err("Unable to recv target info: %d\n", ret); return ret; @@ -230,8 +229,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, /* Determine how many bytes are in the Target's targ_info */ ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->byte_count, - sizeof(targ_info->byte_count), - true); + sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("unable to read target info byte count: %d\n", ret); @@ -252,8 +250,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar, ((u8 *)targ_info) + sizeof(targ_info->byte_count), sizeof(*targ_info) - - sizeof(targ_info->byte_count), - true); + sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("Unable to read target info (%d bytes): %d\n", @@ -311,7 +308,7 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) ret); return ret; } - ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len, true); + ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); @@ -424,7 +421,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) return ret; } - ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), false); + ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; @@ -504,7 +501,7 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) return ret; } - ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), true); + ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; From 50d412346e49aee71b66d90dffb68f8d90ed35b2 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:17 +0300 Subject: [PATCH 065/155] ath6kl: add support for firmware API 2 format In the new format all the format images are embedded into one file. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 20 ++++ drivers/net/wireless/ath/ath6kl/init.c | 136 ++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c6ed1fc42bd9..761e550f0f81 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -58,6 +58,23 @@ #define A_DEFAULT_LISTEN_INTERVAL 100 #define A_MAX_WOW_LISTEN_INTERVAL 1000 +/* includes also the null byte */ +#define ATH6KL_FIRMWARE_MAGIC "QCA-ATH6KL" + +enum ath6kl_fw_ie_type { + ATH6KL_FW_IE_FW_VERSION = 0, + ATH6KL_FW_IE_TIMESTAMP = 1, + ATH6KL_FW_IE_OTP_IMAGE = 2, + ATH6KL_FW_IE_FW_IMAGE = 3, + ATH6KL_FW_IE_PATCH_IMAGE = 4, +}; + +struct ath6kl_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + /* AR6003 1.0 definitions */ #define AR6003_REV1_VERSION 0x300002ba @@ -68,6 +85,7 @@ #define AR6003_REV2_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athwlan.bin.z77" #define AR6003_REV2_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.0/athtcmd_ram.bin" #define AR6003_REV2_PATCH_FILE "ath6k/AR6003/hw2.0/data.patch.bin" +#define AR6003_REV2_FIRMWARE_2_FILE "ath6k/AR6003/hw2.0/fw-2.bin" #define AR6003_REV2_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" #define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin" @@ -77,6 +95,7 @@ #define AR6003_REV3_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athwlan.bin" #define AR6003_REV3_TCMD_FIRMWARE_FILE "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin" #define AR6003_REV3_PATCH_FILE "ath6k/AR6003/hw2.1.1/data.patch.bin" +#define AR6003_REV3_FIRMWARE_2_FILE "ath6k/AR6003/hw2.1.1/fw-2.bin" #define AR6003_REV3_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" #define AR6003_REV3_DEFAULT_BOARD_DATA_FILE \ "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" @@ -84,6 +103,7 @@ /* AR6004 1.0 definitions */ #define AR6004_REV1_VERSION 0x30000623 #define AR6004_REV1_FIRMWARE_FILE "ath6k/AR6004/hw6.1/fw.ram.bin" +#define AR6004_REV1_FIRMWARE_2_FILE "ath6k/AR6004/hw6.1/fw-2.bin" #define AR6004_REV1_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.bin" #define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin" #define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin" diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 4055947ffd67..41f4e0d5858a 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -917,14 +917,10 @@ static int ath6kl_fetch_patch_file(struct ath6kl *ar) return 0; } -static int ath6kl_fetch_firmwares(struct ath6kl *ar) +static int ath6kl_fetch_fw_api1(struct ath6kl *ar) { int ret; - ret = ath6kl_fetch_board_file(ar); - if (ret) - return ret; - ret = ath6kl_fetch_otp_file(ar); if (ret) return ret; @@ -940,6 +936,136 @@ static int ath6kl_fetch_firmwares(struct ath6kl *ar) return 0; } +static int ath6kl_fetch_fw_api2(struct ath6kl *ar) +{ + size_t magic_len, len, ie_len; + const struct firmware *fw; + struct ath6kl_fw_ie *hdr; + const char *filename; + const u8 *data; + int ret, ie_id; + + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + filename = AR6003_REV2_FIRMWARE_2_FILE; + break; + case AR6003_REV3_VERSION: + filename = AR6003_REV3_FIRMWARE_2_FILE; + break; + case AR6004_REV1_VERSION: + filename = AR6004_REV1_FIRMWARE_2_FILE; + break; + default: + return -EOPNOTSUPP; + } + + ret = request_firmware(&fw, filename, ar->dev); + if (ret) + return ret; + + data = fw->data; + len = fw->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ret = -EINVAL; + goto out; + } + + if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { + ret = -EINVAL; + goto out; + } + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath6kl_fw_ie)) { + /* hdr is unaligned! */ + hdr = (struct ath6kl_fw_ie *) data; + + ie_id = le32_to_cpup(&hdr->id); + ie_len = le32_to_cpup(&hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ret = -EINVAL; + goto out; + } + + switch (ie_id) { + case ATH6KL_FW_IE_OTP_IMAGE: + ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw_otp == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_otp_len = ie_len; + break; + case ATH6KL_FW_IE_FW_IMAGE: + ar->fw = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_len = ie_len; + break; + case ATH6KL_FW_IE_PATCH_IMAGE: + ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); + + if (ar->fw_patch == NULL) { + ret = -ENOMEM; + goto out; + } + + ar->fw_patch_len = ie_len; + break; + default: + ath6kl_dbg(ATH6KL_DBG_TRC, "Unknown fw ie: %u\n", + le32_to_cpup(&hdr->id)); + break; + } + + len -= ie_len; + data += ie_len; + }; + + ret = 0; +out: + release_firmware(fw); + + return ret; +} + +static int ath6kl_fetch_firmwares(struct ath6kl *ar) +{ + int ret; + + ret = ath6kl_fetch_board_file(ar); + if (ret) + return ret; + + ret = ath6kl_fetch_fw_api2(ar); + if (ret == 0) + /* fw api 2 found, use it */ + return 0; + + ret = ath6kl_fetch_fw_api1(ar); + if (ret) + return ret; + + return 0; +} + static int ath6kl_upload_board_file(struct ath6kl *ar) { u32 board_address, board_ext_address, param; From a01ac4144e7af80f8c1fd861dc5d280c5687c2a9 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:17 +0300 Subject: [PATCH 066/155] ath6kl: refactor firmware load address code Currently the load address was calculated everytime when it was needed, and with a mess if clauses. Simplify this by adding a field to struct ath6kl for each address and choose the address with simple switch statements. Also move the code just after target version is retrieved. That way it's easier to override the values later in the boot process. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 6 ++ drivers/net/wireless/ath/ath6kl/init.c | 76 ++++++++++++-------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 761e550f0f81..77783f8175e6 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -462,6 +462,12 @@ struct ath6kl { size_t rx_report_len; } tm; + struct { + u32 dataset_patch_addr; + u32 app_load_addr; + u32 app_start_override_addr; + } hw; + u16 conf_flags; wait_queue_head_t event_wq; struct ath6kl_mbox_info mbox_info; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 41f4e0d5858a..f94c049fe214 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -56,12 +56,6 @@ module_param(testmode, uint, 0644); #define CONFIG_AR600x_DEBUG_UART_TX_PIN 8 -enum addr_type { - DATASET_PATCH_ADDR, - APP_LOAD_ADDR, - APP_START_OVERRIDE_ADDR, -}; - #define ATH6KL_DATA_OFFSET 64 struct sk_buff *ath6kl_buf_alloc(int size) { @@ -636,30 +630,6 @@ int ath6kl_unavail_ev(struct ath6kl *ar) } /* firmware upload */ -static u32 ath6kl_get_load_address(u32 target_ver, enum addr_type type) -{ - WARN_ON(target_ver != AR6003_REV2_VERSION && - target_ver != AR6003_REV3_VERSION && - target_ver != AR6004_REV1_VERSION); - - switch (type) { - case DATASET_PATCH_ADDR: - return (target_ver == AR6003_REV2_VERSION) ? - AR6003_REV2_DATASET_PATCH_ADDRESS : - AR6003_REV3_DATASET_PATCH_ADDRESS; - case APP_LOAD_ADDR: - return (target_ver == AR6003_REV2_VERSION) ? - AR6003_REV2_APP_LOAD_ADDRESS : - 0x1234; - case APP_START_OVERRIDE_ADDR: - return (target_ver == AR6003_REV2_VERSION) ? - AR6003_REV2_APP_START_OVERRIDE : - AR6003_REV3_APP_START_OVERRIDE; - default: - return 0; - } -} - static int ath6kl_get_fw(struct ath6kl *ar, const char *filename, u8 **fw, size_t *fw_len) { @@ -1179,8 +1149,7 @@ static int ath6kl_upload_otp(struct ath6kl *ar) if (WARN_ON(ar->fw_otp == NULL)) return -ENOENT; - address = ath6kl_get_load_address(ar->version.target_ver, - APP_LOAD_ADDR); + address = ar->hw.app_load_addr; ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, ar->fw_otp_len); @@ -1191,8 +1160,7 @@ static int ath6kl_upload_otp(struct ath6kl *ar) /* execute the OTP code */ param = 0; - address = ath6kl_get_load_address(ar->version.target_ver, - APP_START_OVERRIDE_ADDR); + address = ar->hw.app_start_override_addr; ath6kl_bmi_execute(ar, address, ¶m); return ret; @@ -1206,8 +1174,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) if (WARN_ON(ar->fw == NULL)) return -ENOENT; - address = ath6kl_get_load_address(ar->version.target_ver, - APP_LOAD_ADDR); + address = ar->hw.app_load_addr; ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); @@ -1221,8 +1188,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) * Don't need to setup app_start override addr on AR6004 */ if (ar->target_type != TARGET_TYPE_AR6004) { - address = ath6kl_get_load_address(ar->version.target_ver, - APP_START_OVERRIDE_ADDR); + address = ar->hw.app_start_override_addr; ath6kl_bmi_set_app_start(ar, address); } return ret; @@ -1236,8 +1202,7 @@ static int ath6kl_upload_patch(struct ath6kl *ar) if (WARN_ON(ar->fw_patch == NULL)) return -ENOENT; - address = ath6kl_get_load_address(ar->version.target_ver, - DATASET_PATCH_ADDR); + address = ar->hw.dataset_patch_addr; ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); if (ret) { @@ -1384,6 +1349,33 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; } +static int ath6kl_init_hw_params(struct ath6kl *ar) +{ + switch (ar->version.target_ver) { + case AR6003_REV2_VERSION: + ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; + ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; + ar->hw.app_start_override_addr = AR6003_REV2_APP_START_OVERRIDE; + break; + case AR6003_REV3_VERSION: + ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; + ar->hw.app_load_addr = 0x1234; + ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; + break; + case AR6004_REV1_VERSION: + ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; + ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; + ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; + break; + default: + ath6kl_err("Unsupported hardware version: 0x%x\n", + ar->version.target_ver); + return -EINVAL; + } + + return 0; +} + static int ath6kl_init(struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); @@ -1523,6 +1515,10 @@ int ath6kl_core_init(struct ath6kl *ar) ar->target_type = le32_to_cpu(targ_info.type); ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version); + ret = ath6kl_init_hw_params(ar); + if (ret) + goto err_bmi_cleanup; + ret = ath6kl_configure_target(ar); if (ret) goto err_bmi_cleanup; From 991b27eaf937a67bb575a95be5c592d9b9109a84 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:17 +0300 Subject: [PATCH 067/155] ath6kl: refactor firmware ext data addr and reserved ram handling size Less if clauses this way and again easier to override the values. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 2 ++ drivers/net/wireless/ath/ath6kl/init.c | 46 +++++++++++--------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 77783f8175e6..3365dc8bab3b 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -466,6 +466,8 @@ struct ath6kl { u32 dataset_patch_addr; u32 app_load_addr; u32 app_start_override_addr; + u32 board_ext_data_addr; + u32 reserved_ram_size; } hw; u16 conf_flags; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index f94c049fe214..bf0385ec0e05 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -527,33 +527,21 @@ int ath6kl_configure_target(struct ath6kl *ar) * but possible in theory. */ - if (ar->target_type == TARGET_TYPE_AR6003 || - ar->target_type == TARGET_TYPE_AR6004) { - if (ar->version.target_ver == AR6003_REV2_VERSION) { - param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; - ram_reserved_size = AR6003_REV2_RAM_RESERVE_SIZE; - } else if (ar->version.target_ver == AR6004_REV1_VERSION) { - param = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; - ram_reserved_size = AR6004_REV1_RAM_RESERVE_SIZE; - } else { - param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; - ram_reserved_size = AR6003_REV3_RAM_RESERVE_SIZE; - } + param = ar->hw.board_ext_data_addr; + ram_reserved_size = ar->hw.reserved_ram_size; - if (ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_board_ext_data)), - (u8 *)¶m, 4) != 0) { - ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); - return -EIO; - } - if (ath6kl_bmi_write(ar, - ath6kl_get_hi_item_addr(ar, - HI_ITEM(hi_end_ram_reserve_sz)), - (u8 *)&ram_reserved_size, 4) != 0) { - ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); - return -EIO; - } + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_board_ext_data)), + (u8 *)¶m, 4) != 0) { + ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); + return -EIO; + } + + if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_end_ram_reserve_sz)), + (u8 *)&ram_reserved_size, 4) != 0) { + ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); + return -EIO; } /* set the block size for the target */ @@ -1356,16 +1344,22 @@ static int ath6kl_init_hw_params(struct ath6kl *ar) ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; ar->hw.app_start_override_addr = AR6003_REV2_APP_START_OVERRIDE; + ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; + ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; break; case AR6003_REV3_VERSION: ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = 0x1234; ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; + ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; + ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; break; case AR6004_REV1_VERSION: ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; + ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; + ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; break; default: ath6kl_err("Unsupported hardware version: 0x%x\n", From 639d0b8996aa5913402b846932d57a51a23a40c9 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 12 Sep 2011 12:48:09 +0300 Subject: [PATCH 068/155] ath6kl: read firmware start address from hardware It's actually possible to read the firmware start address from hardware, that way there's no need to hardcode the address in hardware. Thanks to Chilam Ng for the idea. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 17 +++++++++++++---- drivers/net/wireless/ath/ath6kl/target.h | 2 -- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index bf0385ec0e05..5865466e884c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1146,9 +1146,21 @@ static int ath6kl_upload_otp(struct ath6kl *ar) return ret; } + /* read firmware start address */ + ret = ath6kl_bmi_read(ar, + ath6kl_get_hi_item_addr(ar, + HI_ITEM(hi_app_start)), + (u8 *) &address, sizeof(address)); + + if (ret) { + ath6kl_err("Failed to read hi_app_start: %d\n", ret); + return ret; + } + + ar->hw.app_start_override_addr = address; + /* execute the OTP code */ param = 0; - address = ar->hw.app_start_override_addr; ath6kl_bmi_execute(ar, address, ¶m); return ret; @@ -1343,21 +1355,18 @@ static int ath6kl_init_hw_params(struct ath6kl *ar) case AR6003_REV2_VERSION: ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; - ar->hw.app_start_override_addr = AR6003_REV2_APP_START_OVERRIDE; ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; break; case AR6003_REV3_VERSION: ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = 0x1234; - ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; break; case AR6004_REV1_VERSION: ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; - ar->hw.app_start_override_addr = AR6003_REV3_APP_START_OVERRIDE; ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; break; diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 7db06a5d9194..c9a76051f042 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -331,13 +331,11 @@ struct host_interest { (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \ (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0)) -#define AR6003_REV2_APP_START_OVERRIDE 0x944C00 #define AR6003_REV2_APP_LOAD_ADDRESS 0x543180 #define AR6003_REV2_BOARD_EXT_DATA_ADDRESS 0x57E500 #define AR6003_REV2_DATASET_PATCH_ADDRESS 0x57e884 #define AR6003_REV2_RAM_RESERVE_SIZE 6912 -#define AR6003_REV3_APP_START_OVERRIDE 0x945d00 #define AR6003_REV3_APP_LOAD_ADDRESS 0x545000 #define AR6003_REV3_BOARD_EXT_DATA_ADDRESS 0x542330 #define AR6003_REV3_DATASET_PATCH_ADDRESS 0x57FF74 From 8a13748034e93b4134455ebf51e2fada8eb00aca Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 7 Sep 2011 10:55:17 +0300 Subject: [PATCH 069/155] ath6kl: read reserved ram size from firmware file A new version of firmware needs different reserved ram size so read that from the firmware image. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 3365dc8bab3b..abb4aaf48c08 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -67,6 +67,7 @@ enum ath6kl_fw_ie_type { ATH6KL_FW_IE_OTP_IMAGE = 2, ATH6KL_FW_IE_FW_IMAGE = 3, ATH6KL_FW_IE_PATCH_IMAGE = 4, + ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5, }; struct ath6kl_fw_ie { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 5865466e884c..e2a29b25884c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -902,6 +902,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) const char *filename; const u8 *data; int ret, ie_id; + __le32 *val; switch (ar->version.target_ver) { case AR6003_REV2_VERSION: @@ -987,6 +988,10 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->fw_patch_len = ie_len; break; + case ATH6KL_FW_IE_RESERVED_RAM_SIZE: + val = (__le32 *) data; + ar->hw.reserved_ram_size = le32_to_cpup(val); + break; default: ath6kl_dbg(ATH6KL_DBG_TRC, "Unknown fw ie: %u\n", le32_to_cpup(&hdr->id)); From 97e0496d056726ab46e7e977315f2ab847b34209 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 12 Sep 2011 13:47:34 +0300 Subject: [PATCH 070/155] ath6kl: add firmware capabilities support The new firmware format includes capability bits which make it possible to check what features the firmware supports. Add infrastructure to read the capabilities. For now it only provides ATH6KL_FW_CAPABILITY_HOST_P2P which is not even used anywhere yet, but that will be added later. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 12 ++++++++++++ drivers/net/wireless/ath/ath6kl/init.c | 11 ++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index abb4aaf48c08..0fb82e9002be 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -68,8 +68,18 @@ enum ath6kl_fw_ie_type { ATH6KL_FW_IE_FW_IMAGE = 3, ATH6KL_FW_IE_PATCH_IMAGE = 4, ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5, + ATH6KL_FW_IE_CAPABILITIES = 6, }; +enum ath6kl_fw_capability { + ATH6KL_FW_CAPABILITY_HOST_P2P = 0, + + /* this needs to be last */ + ATH6KL_FW_CAPABILITY_MAX, +}; + +#define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32) + struct ath6kl_fw_ie { __le32 id; __le32 len; @@ -491,6 +501,8 @@ struct ath6kl { u8 *fw_patch; size_t fw_patch_len; + unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN]; + struct workqueue_struct *ath6kl_wq; struct ath6kl_node_table scan_table; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index e2a29b25884c..b9b13a040c7e 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -901,7 +901,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) struct ath6kl_fw_ie *hdr; const char *filename; const u8 *data; - int ret, ie_id; + int ret, ie_id, i, index, bit; __le32 *val; switch (ar->version.target_ver) { @@ -992,6 +992,15 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) val = (__le32 *) data; ar->hw.reserved_ram_size = le32_to_cpup(val); break; + case ATH6KL_FW_IE_CAPABILITIES: + for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { + index = ALIGN(i, 8) / 8; + bit = i % 8; + + if (data[index] & (1 << bit)) + __set_bit(i, ar->fw_capabilities); + } + break; default: ath6kl_dbg(ATH6KL_DBG_TRC, "Unknown fw ie: %u\n", le32_to_cpup(&hdr->id)); From ac59a2b285abbcec1ec487ef56dcc25c654853fb Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 10 Sep 2011 15:26:34 +0530 Subject: [PATCH 071/155] ath6kl: Remove auth type fall back in auto authentication mode Target already tries with different authentication mechanism when authentication type is configured to NL80211_AUTHTYPE_AUTOMATIC. Remove this piece of code from driver. Having this code in driver even affects auto + WEP authentication in some cases. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 74 ++++------------------ drivers/net/wireless/ath/ath6kl/core.h | 2 - drivers/net/wireless/ath/ath6kl/init.c | 1 - drivers/net/wireless/ath/ath6kl/wmi.h | 5 -- 4 files changed, 12 insertions(+), 70 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a889bf4a4722..fcef417884b8 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -158,8 +158,7 @@ static int ath6kl_set_auth_type(struct ath6kl *ar, break; case NL80211_AUTHTYPE_AUTOMATIC: - ar->dot11_auth_mode = OPEN_AUTH; - ar->auto_auth_stage = AUTH_OPEN_IN_PROGRESS; + ar->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH; break; default: @@ -446,8 +445,6 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset; - ar->auto_auth_stage = AUTH_IDLE; - if (nw_type & ADHOC_NETWORK) { if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, @@ -599,9 +596,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 proto_reason) { - struct ath6kl_key *key = NULL; - u16 status; - if (ar->scan_req) { cfg80211_scan_done(ar->scan_req, true); ar->scan_req = NULL; @@ -643,64 +637,20 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, if (reason != DISCONNECT_CMD) return; - if (!ar->auto_auth_stage) { - clear_bit(CONNECT_PEND, &ar->flag); + clear_bit(CONNECT_PEND, &ar->flag); - if (ar->sme_state == SME_CONNECTING) { - cfg80211_connect_result(ar->net_dev, - bssid, NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); - } else if (ar->sme_state == SME_CONNECTED) { - cfg80211_disconnected(ar->net_dev, reason, - NULL, 0, GFP_KERNEL); - } - - ar->sme_state = SME_DISCONNECTED; - return; + if (ar->sme_state == SME_CONNECTING) { + cfg80211_connect_result(ar->net_dev, + bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } else if (ar->sme_state == SME_CONNECTED) { + cfg80211_disconnected(ar->net_dev, reason, + NULL, 0, GFP_KERNEL); } - if (ar->dot11_auth_mode != OPEN_AUTH) - return; - - /* - * If the current auth algorithm is open, try shared and - * make autoAuthStage idle. We do not make it leap for now - * being. - */ - key = &ar->keys[ar->def_txkey_index]; - if (down_interruptible(&ar->sem)) { - ath6kl_err("busy, couldn't get access\n"); - return; - } - - ar->dot11_auth_mode = SHARED_AUTH; - ar->auto_auth_stage = AUTH_IDLE; - - ath6kl_wmi_addkey_cmd(ar->wmi, - ar->def_txkey_index, - ar->prwise_crypto, - GROUP_USAGE | TX_USAGE, - key->key_len, NULL, - key->key, - KEY_OP_INIT_VAL, NULL, - NO_SYNC_WMIFLAG); - - status = ath6kl_wmi_connect_cmd(ar->wmi, - ar->nw_type, - ar->dot11_auth_mode, - ar->auth_mode, - ar->prwise_crypto, - ar->prwise_crypto_len, - ar->grp_crypto, - ar->grp_crypto_len, - ar->ssid_len, - ar->ssid, - ar->req_bssid, - ar->ch_hint, - ar->connect_ctrl_flags); - up(&ar->sem); + ar->sme_state = SME_DISCONNECTED; } static inline bool is_ch_11a(u16 ch) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 0fb82e9002be..a9b3b17ef3ef 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -466,8 +466,6 @@ struct ath6kl { enum wlan_low_pwr_state wlan_pwr_state; struct wmi_scan_params_cmd sc_params; #define AR_MCAST_FILTER_MAC_ADDR_SIZE 4 - u8 auto_auth_stage; - struct { void *rx_report; size_t rx_report_len; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index b9b13a040c7e..1834e9af5799 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -586,7 +586,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev) SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); wdev->netdev = dev; ar->sme_state = SME_DISCONNECTED; - ar->auto_auth_stage = AUTH_IDLE; init_netdev(dev); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index dc49ef86c1c8..f036e78522ab 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -566,11 +566,6 @@ enum dot11_auth_mode { LEAP_AUTH = 0x04, }; -enum { - AUTH_IDLE, - AUTH_OPEN_IN_PROGRESS, -}; - enum auth_mode { NONE_AUTH = 0x01, WPA_AUTH = 0x02, From 170826dd0d9fa71b648aa31ecb1e2973d777dbdb Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 10 Sep 2011 15:26:35 +0530 Subject: [PATCH 072/155] ath6kl: Set the sme_state to disconnected in disconnect() callback After a successful completion of disconnect() driver needs to set it's sme_state to SME_DISCONNECTED to be in sync with cfg80211 state. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index fcef417884b8..17bb8e28b338 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -589,6 +589,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, up(&ar->sem); + ar->sme_state = SME_DISCONNECTED; + return 0; } From 151411e88fe1d1a729a4f706a2aebef8bc000a69 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 15 Sep 2011 15:10:16 +0300 Subject: [PATCH 073/155] ath6kl: Fix static WEP configuration in AP mode Configuration of the WEP keys needs to be delayed until the AP mode has been properly started at the target. Partial support for delaying the WEP key configuration was already in place in the driver, but the actual part of deciding when to do this was missing. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 16 +++++++++++++++- drivers/net/wireless/ath/ath6kl/main.c | 4 +++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 17bb8e28b338..e196097e7524 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -904,6 +904,20 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, } } + if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT && + !test_bit(CONNECTED, &ar->flag)) { + /* + * Store the key locally so that it can be re-configured after + * the AP mode has properly started + * (ath6kl_install_statioc_wep_keys). + */ + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration " + "until AP mode has been started\n"); + ar->wep_key_list[key_index].key_len = key->key_len; + memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len); + return 0; + } + status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, key_type, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, @@ -1018,7 +1032,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, if (multicast) key_type = ar->grp_crypto; - if (ar->nw_type == AP_NETWORK && !test_bit(CONNECTED, &ar->flag)) + if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag)) return 0; /* Delay until AP mode has been started */ status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index d510046c99d6..acbd35d8df2b 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1364,8 +1364,10 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL); } - if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) + if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) { + memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list)); clear_bit(CONNECTED, &ar->flag); + } return; } From 9df337a104ab99c595cc4ede2c917ba1c2b66374 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Thu, 15 Sep 2011 20:30:43 +0530 Subject: [PATCH 074/155] ath6kl: deinitialise wiphy on error This fixes the following panic observed on card removal. BUG: unable to handle kernel paging request at f86e22ac EIP is at wiphy_update_regulatory+0x252/0x590 [cfg80211] Call Trace: [] set_regdom+0x165/0x600 [cfg80211] [] ? __kmalloc+0x10a/0x190 [] ? nla_parse+0xb7/0xd0 [] ? T.1400+0x12/0x20 [cfg80211] [] nl80211_set_reg+0xe4/0x270 [cfg80211] [] ? nl80211_pre_doit+0x0/0x160 [cfg80211] [] genl_rcv_msg+0x23b/0x280 [] ? genl_rcv_msg+0x0/0x280 [] netlink_rcv_skb+0x86/0xb0 [] ? genl_rcv+0x0/0x30 [] genl_rcv+0x1c/0x30 Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/sdio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 0cce80169670..4724ddfab4f7 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -25,6 +25,7 @@ #include "hif-ops.h" #include "target.h" #include "debug.h" +#include "cfg80211.h" struct ath6kl_sdio { struct sdio_func *func; @@ -816,7 +817,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ath6kl_err("Failed to enable 4-bit async irq mode %d\n", ret); sdio_release_host(func); - goto err_dma; + goto err_cfg80211; } ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n"); @@ -829,7 +830,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, ret = ath6kl_sdio_power_on(ar_sdio); if (ret) - goto err_dma; + goto err_cfg80211; sdio_claim_host(func); @@ -853,6 +854,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func, err_off: ath6kl_sdio_power_off(ar_sdio); +err_cfg80211: + ath6kl_cfg80211_deinit(ar_sdio->ar); err_dma: kfree(ar_sdio->dma_buffer); err_hif: From 01cac476a4bb07b5b6f205b15809e0a845574653 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:14:59 +0300 Subject: [PATCH 075/155] ath6kl: Fix BSS update on roaming This fixes the BSS "update" just before the connected or roamed event. The previous implementation was completely broken: it forced a hardcoded signal strength and IEs from Association _Request_ frame instead of any Beacon information. This broke various things, including PMKSA caching. The current workaround for creating a dummy BSS entry before the roamed event is not exactly ideal, but that is quite a bit better than the previous state. As a future improvement, cfg80211 could potentially be extended to allow this type of use or ath6kl could delay sending the roamed event before receiving a BSS info event. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 143 ++++++++------------- 1 file changed, 57 insertions(+), 86 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e196097e7524..5ede3d2f1f2a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -413,6 +413,53 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid, + struct ieee80211_channel *chan, + const u8 *beacon_ie, size_t beacon_ie_len) +{ + struct cfg80211_bss *bss; + u8 *ie; + + bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid, + ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (bss == NULL) { + /* + * Since cfg80211 may not yet know about the BSS, + * generate a partial entry until the first BSS info + * event becomes available. + * + * Prepend SSID element since it is not included in the Beacon + * IEs from the target. + */ + ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL); + if (ie == NULL) + return -ENOMEM; + ie[0] = WLAN_EID_SSID; + ie[1] = ar->ssid_len; + memcpy(ie + 2, ar->ssid, ar->ssid_len); + memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len); + bss = cfg80211_inform_bss(ar->wdev->wiphy, chan, + bssid, 0, WLAN_CAPABILITY_ESS, 100, + ie, 2 + ar->ssid_len + beacon_ie_len, + 0, GFP_KERNEL); + if (bss) + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for " + "%pM prior to indicating connect/roamed " + "event\n", bssid); + kfree(ie); + } else + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss " + "entry\n"); + + if (bss == NULL) + return -ENOMEM; + + cfg80211_put_bss(bss); + + return 0; +} + void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, u16 listen_intvl, u16 beacon_intvl, @@ -420,17 +467,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, u8 beacon_ie_len, u8 assoc_req_len, u8 assoc_resp_len, u8 *assoc_info) { - u16 size = 0; - u16 capability = 0; - struct cfg80211_bss *bss = NULL; - struct ieee80211_mgmt *mgmt = NULL; - struct ieee80211_channel *ibss_ch = NULL; - s32 signal = 50 * 100; - u8 ie_buf_len = 0; - unsigned char ie_buf[256]; - unsigned char *ptr_ie_buf = ie_buf; - unsigned char *ieeemgmtbuf = NULL; - u8 source_mac[ETH_ALEN]; + struct ieee80211_channel *chan; /* capinfo + listen interval */ u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16); @@ -462,87 +499,21 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, } } - /* - * Earlier we were updating the cfg about bss by making a beacon frame - * only if the entry for bss is not there. This can have some issue if - * ROAM event is generated and a heavy traffic is ongoing. The ROAM - * event is handled through a work queue and by the time it really gets - * handled, BSS would have been aged out. So it is better to update the - * cfg about BSS irrespective of its entry being present right now or - * not. - */ + chan = ieee80211_get_channel(ar->wdev->wiphy, (int) channel); - if (nw_type & ADHOC_NETWORK) { - /* construct 802.11 mgmt beacon */ - if (ptr_ie_buf) { - *ptr_ie_buf++ = WLAN_EID_SSID; - *ptr_ie_buf++ = ar->ssid_len; - memcpy(ptr_ie_buf, ar->ssid, ar->ssid_len); - ptr_ie_buf += ar->ssid_len; - - *ptr_ie_buf++ = WLAN_EID_IBSS_PARAMS; - *ptr_ie_buf++ = 2; /* length */ - *ptr_ie_buf++ = 0; /* ATIM window */ - *ptr_ie_buf++ = 0; /* ATIM window */ - - /* TODO: update ibss params and include supported rates, - * DS param set, extened support rates, wmm. */ - - ie_buf_len = ptr_ie_buf - ie_buf; - } - - capability |= WLAN_CAPABILITY_IBSS; - - if (ar->prwise_crypto == WEP_CRYPT) - capability |= WLAN_CAPABILITY_PRIVACY; - - memcpy(source_mac, ar->net_dev->dev_addr, ETH_ALEN); - ptr_ie_buf = ie_buf; - } else { - capability = *(u16 *) (&assoc_info[beacon_ie_len]); - memcpy(source_mac, bssid, ETH_ALEN); - ptr_ie_buf = assoc_req_ie; - ie_buf_len = assoc_req_len; - } - - size = offsetof(struct ieee80211_mgmt, u) - + sizeof(mgmt->u.beacon) - + ie_buf_len; - - ieeemgmtbuf = kzalloc(size, GFP_ATOMIC); - if (!ieeemgmtbuf) { - ath6kl_err("ieee mgmt buf alloc error\n"); - return; - } - - mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf; - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_BEACON); - memset(mgmt->da, 0xff, ETH_ALEN); /* broadcast addr */ - memcpy(mgmt->sa, source_mac, ETH_ALEN); - memcpy(mgmt->bssid, bssid, ETH_ALEN); - mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_intvl); - mgmt->u.beacon.capab_info = cpu_to_le16(capability); - memcpy(mgmt->u.beacon.variable, ptr_ie_buf, ie_buf_len); - - ibss_ch = ieee80211_get_channel(ar->wdev->wiphy, (int)channel); - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, - "%s: inform bss with bssid %pM channel %d beacon_intvl %d capability 0x%x\n", - __func__, mgmt->bssid, ibss_ch->hw_value, - beacon_intvl, capability); - - bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, - ibss_ch, mgmt, - size, signal, GFP_KERNEL); - kfree(ieeemgmtbuf); - cfg80211_put_bss(bss); if (nw_type & ADHOC_NETWORK) { cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL); return; } + if (ath6kl_add_bss_if_needed(ar, bssid, chan, assoc_info, + beacon_ie_len) < 0) { + ath6kl_err("could not add cfg80211 bss entry for " + "connect/roamed notification\n"); + return; + } + if (ar->sme_state == SME_CONNECTING) { /* inform connect result to cfg80211 */ ar->sme_state = SME_CONNECTED; @@ -552,7 +523,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, WLAN_STATUS_SUCCESS, GFP_KERNEL); } else if (ar->sme_state == SME_CONNECTED) { /* inform roam event to cfg80211 */ - cfg80211_roamed(ar->net_dev, ibss_ch, bssid, + cfg80211_roamed(ar->net_dev, chan, bssid, assoc_req_ie, assoc_req_len, assoc_resp_ie, assoc_resp_len, GFP_KERNEL); } From f195d5076a734c6d96a0dd80fe2a3b1e608e7979 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:00 +0300 Subject: [PATCH 076/155] ath6kl: Remove deprecated WMI_OPT_RX_FRAME_EVENTID processing This event has been deprecated and there is no need for ath6kl to include code for processing it. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 40 +-------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index dbddb91389d0..24f0e3eb4211 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1185,44 +1185,6 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) return 0; } -static int ath6kl_wmi_opt_frame_event_rx(struct wmi *wmi, u8 *datap, int len) -{ - struct bss *bss; - struct wmi_opt_rx_info_hdr *bih; - u8 *buf; - - if (len <= sizeof(struct wmi_opt_rx_info_hdr)) - return -EINVAL; - - bih = (struct wmi_opt_rx_info_hdr *) datap; - buf = datap + sizeof(struct wmi_opt_rx_info_hdr); - len -= sizeof(struct wmi_opt_rx_info_hdr); - - ath6kl_dbg(ATH6KL_DBG_WMI, "opt frame event %2.2x:%2.2x\n", - bih->bssid[4], bih->bssid[5]); - - bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid); - if (bss != NULL) { - /* Free up the node. We are about to allocate a new node. */ - wlan_node_reclaim(&wmi->parent_dev->scan_table, bss); - } - - bss = wlan_node_alloc(len); - if (!bss) - return -ENOMEM; - - bss->ni_snr = bih->snr; - bss->ni_cie.ie_chan = le16_to_cpu(bih->ch); - - if (WARN_ON(!bss->ni_buf)) - return -EINVAL; - - memcpy(bss->ni_buf, buf, len); - wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid); - - return 0; -} - /* Inactivity timeout of a fatpipe(pstream) at the target */ static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap, int len) @@ -3175,7 +3137,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_OPT_RX_FRAME_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n"); - ret = ath6kl_wmi_opt_frame_event_rx(wmi, datap, len); + /* this event has been deprecated */ break; case WMI_REPORT_ROAM_TBL_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n"); From 64b834d83a191dd6585c0778b1a7a92c36775554 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:01 +0300 Subject: [PATCH 077/155] ath6kl: Remove RSSI update for internal node table ath6kl does not actually update cfg80211 BSS table when this update occurs, so there is not much need in updating the internal table that is not used or exposed. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index acbd35d8df2b..f21e4b12544c 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1091,26 +1091,11 @@ static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len) (struct wmi_target_stats *) ptr; struct target_stats *stats = &ar->target_stats; struct tkip_ccmp_stats *ccmp_stats; - struct bss *conn_bss = NULL; - struct cserv_stats *c_stats; u8 ac; if (len < sizeof(*tgt_stats)) return; - /* update the RSSI of the connected bss */ - if (test_bit(CONNECTED, &ar->flag)) { - conn_bss = ath6kl_wmi_find_node(ar->wmi, ar->bssid); - if (conn_bss) { - c_stats = &tgt_stats->cserv_stats; - conn_bss->ni_rssi = - a_sle16_to_cpu(c_stats->cs_ave_beacon_rssi); - conn_bss->ni_snr = - tgt_stats->cserv_stats.cs_ave_beacon_snr; - ath6kl_wmi_node_return(ar->wmi, conn_bss); - } - } - ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n"); stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt); From 3b25ed186fc3ac8d2517332bfbd5c44016c10f82 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:02 +0300 Subject: [PATCH 078/155] ath6kl: Remove unnecessary node table update on disconnect event Since ath6kl does not actually update cfg80211 BSS table when this event occurs, there is not much need for removing the entries from the internal table that is not really used or exposed. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/main.c | 28 -------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index f21e4b12544c..55d3331bed85 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1326,7 +1326,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 prot_reason_status) { - struct bss *wmi_ssid_node = NULL; unsigned long flags; if (ar->nw_type == AP_NETWORK) { @@ -1386,33 +1385,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid, } } - if ((reason == NO_NETWORK_AVAIL) && test_bit(WMI_READY, &ar->flag)) { - ath6kl_wmi_node_free(ar->wmi, bssid); - - /* - * In case any other same SSID nodes are present remove it, - * since those nodes also not available now. - */ - do { - /* - * Find the nodes based on SSID and remove it - * - * Note: This case will not work out for - * Hidden-SSID - */ - wmi_ssid_node = ath6kl_wmi_find_ssid_node(ar->wmi, - ar->ssid, - ar->ssid_len, - false, - true); - - if (wmi_ssid_node) - ath6kl_wmi_node_free(ar->wmi, - wmi_ssid_node->ni_macaddr); - - } while (wmi_ssid_node); - } - /* update connect & link status atomically */ spin_lock_irqsave(&ar->lock, flags); clear_bit(CONNECTED, &ar->flag); From 1aaa8c7469db14c3cbb0776afda0fb007eb43f46 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:03 +0300 Subject: [PATCH 079/155] ath6kl: Replace internal node table with cfg80211 BSS table The internal node table in ath6kl was not really used for any useful purpose. It was just used to collect scan results during a scan and then provide them in a burst to cfg80211 at the completion of the scan. There is no point in doing this since cfg80211 is perfectly capable of maintaining the BSS table and the BSS inform messages are sent in separate function calls anyway. This provides more complete information in the cfg80211 BSS table since this allows Beacon and Probe Response frames to be distinguished and IEs from them reported separately. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 296 ++++---------------------- 1 file changed, 45 insertions(+), 251 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 24f0e3eb4211..ff13e0bc646b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -910,277 +910,74 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len) return 0; } -static int ath6kl_wlan_parse_beacon(u8 *buf, int frame_len, - struct ath6kl_common_ie *cie) -{ - u8 *frm, *efrm; - u8 elemid_ssid = false; - - frm = buf; - efrm = (u8 *) (frm + frame_len); - - /* - * beacon/probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] capability information - * [tlv] ssid - * [tlv] supported rates - * [tlv] country information - * [tlv] parameter set (FH/DS) - * [tlv] erp information - * [tlv] extended supported rates - * [tlv] WMM - * [tlv] WPA or RSN - * [tlv] Atheros Advanced Capabilities - */ - if ((efrm - frm) < 12) - return -EINVAL; - - memset(cie, 0, sizeof(*cie)); - - cie->ie_tstamp = frm; - frm += 8; - cie->ie_beaconInt = *(u16 *) frm; - frm += 2; - cie->ie_capInfo = *(u16 *) frm; - frm += 2; - cie->ie_chan = 0; - - while (frm < efrm) { - switch (*frm) { - case WLAN_EID_SSID: - if (!elemid_ssid) { - cie->ie_ssid = frm; - elemid_ssid = true; - } - break; - case WLAN_EID_SUPP_RATES: - cie->ie_rates = frm; - break; - case WLAN_EID_COUNTRY: - cie->ie_country = frm; - break; - case WLAN_EID_FH_PARAMS: - break; - case WLAN_EID_DS_PARAMS: - cie->ie_chan = frm[2]; - break; - case WLAN_EID_TIM: - cie->ie_tim = frm; - break; - case WLAN_EID_IBSS_PARAMS: - break; - case WLAN_EID_EXT_SUPP_RATES: - cie->ie_xrates = frm; - break; - case WLAN_EID_ERP_INFO: - if (frm[1] != 1) - return -EINVAL; - - cie->ie_erp = frm[2]; - break; - case WLAN_EID_RSN: - cie->ie_rsn = frm; - break; - case WLAN_EID_HT_CAPABILITY: - cie->ie_htcap = frm; - break; - case WLAN_EID_HT_INFORMATION: - cie->ie_htop = frm; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (frm[1] > 3 && frm[2] == 0x00 && frm[3] == 0x50 && - frm[4] == 0xf2) { - /* OUT Type (00:50:F2) */ - - if (frm[5] == WPA_OUI_TYPE) { - /* WPA OUT */ - cie->ie_wpa = frm; - } else if (frm[5] == WMM_OUI_TYPE) { - /* WMM OUT */ - cie->ie_wmm = frm; - } else if (frm[5] == WSC_OUT_TYPE) { - /* WSC OUT */ - cie->ie_wsc = frm; - } - - } else if (frm[1] > 3 && frm[2] == 0x00 - && frm[3] == 0x03 && frm[4] == 0x7f - && frm[5] == ATH_OUI_TYPE) { - /* Atheros OUI (00:03:7f) */ - cie->ie_ath = frm; - } - break; - default: - break; - } - frm += frm[1] + 2; - } - - if ((cie->ie_rates == NULL) - || (cie->ie_rates[1] > ATH6KL_RATE_MAXSIZE)) - return -EINVAL; - - if ((cie->ie_ssid == NULL) - || (cie->ie_ssid[1] > IEEE80211_MAX_SSID_LEN)) - return -EINVAL; - - return 0; -} - static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) { - struct bss *bss = NULL; struct wmi_bss_info_hdr *bih; - u8 cached_ssid_len = 0; - u8 cached_ssid[IEEE80211_MAX_SSID_LEN] = { 0 }; - u8 beacon_ssid_len = 0; - u8 *buf, *ie_ssid; - u8 *ni_buf; - int buf_len; - - int ret; + u8 *buf; + struct ieee80211_channel *channel; + struct ath6kl *ar = wmi->parent_dev; + struct ieee80211_mgmt *mgmt; + struct cfg80211_bss *bss; if (len <= sizeof(struct wmi_bss_info_hdr)) return -EINVAL; bih = (struct wmi_bss_info_hdr *) datap; - bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid); - - if (a_sle16_to_cpu(bih->rssi) > 0) { - if (bss == NULL) - return 0; - else - bih->rssi = a_cpu_to_sle16(bss->ni_rssi); - } - buf = datap + sizeof(struct wmi_bss_info_hdr); len -= sizeof(struct wmi_bss_info_hdr); ath6kl_dbg(ATH6KL_DBG_WMI, - "bss info evt - ch %u, rssi %02x, bssid \"%pM\"\n", - bih->ch, a_sle16_to_cpu(bih->rssi), bih->bssid); + "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" " + "frame_type=%d\n", + bih->ch, bih->snr, a_sle16_to_cpu(bih->rssi), bih->bssid, + bih->frame_type); - if (bss != NULL) { - /* - * Free up the node. We are about to allocate a new node. - * In case of hidden AP, beacon will not have ssid, - * but a directed probe response will have it, - * so cache the probe-resp-ssid if already present. - */ - if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE)) { - ie_ssid = bss->ni_cie.ie_ssid; - if (ie_ssid && (ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) && - (ie_ssid[2] != 0)) { - cached_ssid_len = ie_ssid[1]; - memcpy(cached_ssid, ie_ssid + 2, - cached_ssid_len); - } - } + if (bih->frame_type != BEACON_FTYPE && + bih->frame_type != PROBERESP_FTYPE) + return 0; /* Only update BSS table for now */ - /* - * Use the current average rssi of associated AP base on - * assumption - * 1. Most os with GUI will update RSSI by - * ath6kl_wmi_get_stats_cmd() periodically. - * 2. ath6kl_wmi_get_stats_cmd(..) will be called when calling - * ath6kl_wmi_startscan_cmd(...) - * The average value of RSSI give end-user better feeling for - * instance value of scan result. It also sync up RSSI info - * in GUI between scan result and RSSI signal icon. - */ - if (memcmp(wmi->parent_dev->bssid, bih->bssid, ETH_ALEN) == 0) { - bih->rssi = a_cpu_to_sle16(bss->ni_rssi); - bih->snr = bss->ni_snr; - } + channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch)); + if (channel == NULL) + return -EINVAL; - wlan_node_reclaim(&wmi->parent_dev->scan_table, bss); - } + if (len < 8 + 2 + 2) + return -EINVAL; /* - * beacon/probe response frame format - * [8] time stamp - * [2] beacon interval - * [2] capability information - * [tlv] ssid + * In theory, use of cfg80211_inform_bss() would be more natural here + * since we do not have the full frame. However, at least for now, + * cfg80211 can only distinguish Beacon and Probe Response frames from + * each other when using cfg80211_inform_bss_frame(), so let's build a + * fake IEEE 802.11 header to be able to take benefit of this. */ - beacon_ssid_len = buf[SSID_IE_LEN_INDEX]; + mgmt = kmalloc(24 + len, GFP_ATOMIC); + if (mgmt == NULL) + return -EINVAL; - /* - * If ssid is cached for this hidden AP, then change - * buffer len accordingly. - */ - if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) && - (cached_ssid_len != 0) && - (beacon_ssid_len == 0 || (cached_ssid_len > beacon_ssid_len && - buf[SSID_IE_LEN_INDEX + 1] == 0))) { + if (bih->frame_type == BEACON_FTYPE) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + memset(mgmt->da, 0xff, ETH_ALEN); + } else { + struct net_device *dev = ar->net_dev; - len += (cached_ssid_len - beacon_ssid_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + memcpy(mgmt->da, dev->dev_addr, ETH_ALEN); } + mgmt->duration = cpu_to_le16(0); + memcpy(mgmt->sa, bih->bssid, ETH_ALEN); + memcpy(mgmt->bssid, bih->bssid, ETH_ALEN); + mgmt->seq_ctrl = cpu_to_le16(0); - bss = wlan_node_alloc(len); - if (!bss) + memcpy(&mgmt->u.beacon, buf, len); + + bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt, + 24 + len, bih->snr * 100, GFP_ATOMIC); + kfree(mgmt); + if (bss == NULL) return -ENOMEM; - - bss->ni_snr = bih->snr; - bss->ni_rssi = a_sle16_to_cpu(bih->rssi); - - if (WARN_ON(!bss->ni_buf)) - return -EINVAL; - - /* - * In case of hidden AP, beacon will not have ssid, - * but a directed probe response will have it, - * so place the cached-ssid(probe-resp) in the bss info. - */ - if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) && - (cached_ssid_len != 0) && - (beacon_ssid_len == 0 || (beacon_ssid_len && - buf[SSID_IE_LEN_INDEX + 1] == 0))) { - ni_buf = bss->ni_buf; - buf_len = len; - - /* - * Copy the first 14 bytes: - * time-stamp(8), beacon-interval(2), - * cap-info(2), ssid-id(1), ssid-len(1). - */ - memcpy(ni_buf, buf, SSID_IE_LEN_INDEX + 1); - - ni_buf[SSID_IE_LEN_INDEX] = cached_ssid_len; - ni_buf += (SSID_IE_LEN_INDEX + 1); - - buf += (SSID_IE_LEN_INDEX + 1); - buf_len -= (SSID_IE_LEN_INDEX + 1); - - memcpy(ni_buf, cached_ssid, cached_ssid_len); - ni_buf += cached_ssid_len; - - buf += beacon_ssid_len; - buf_len -= beacon_ssid_len; - - if (cached_ssid_len > beacon_ssid_len) - buf_len -= (cached_ssid_len - beacon_ssid_len); - - memcpy(ni_buf, buf, buf_len); - } else - memcpy(bss->ni_buf, buf, len); - - bss->ni_framelen = len; - - ret = ath6kl_wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie); - if (ret) { - wlan_node_free(bss); - return -EINVAL; - } - - /* - * Update the frequency in ie_chan, overwriting of channel number - * which is done in ath6kl_wlan_parse_beacon - */ - bss->ni_cie.ie_chan = le16_to_cpu(bih->ch); - wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid); + cfg80211_put_bss(bss); return 0; } @@ -1295,9 +1092,6 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len) ev = (struct wmi_scan_complete_event *) datap; - if (a_sle32_to_cpu(ev->status) == 0) - wlan_refresh_inactive_nodes(wmi->parent_dev); - ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status)); wmi->is_probe_ssid = false; From 457fb0415a887b6389854b850e9815cf0ec44178 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:04 +0300 Subject: [PATCH 080/155] ath6kl: Remove the unused node table implementation Now that the scan results are reported directly to the cfg80211 BSS table there is no need for maintaining this internal node table implementation for scan results. Remove the definitions and node table functions. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/Makefile | 1 - drivers/net/wireless/ath/ath6kl/cfg80211.c | 52 ----- drivers/net/wireless/ath/ath6kl/common.h | 83 -------- drivers/net/wireless/ath/ath6kl/core.h | 2 - drivers/net/wireless/ath/ath6kl/init.c | 5 - drivers/net/wireless/ath/ath6kl/node.c | 234 --------------------- drivers/net/wireless/ath/ath6kl/wmi.c | 37 ---- drivers/net/wireless/ath/ath6kl/wmi.h | 8 - 8 files changed, 422 deletions(-) delete mode 100644 drivers/net/wireless/ath/ath6kl/node.c diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 5fe092046d3e..8f7a0d1c290c 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -31,7 +31,6 @@ ath6kl-y += init.o ath6kl-y += main.o ath6kl-y += txrx.o ath6kl-y += wmi.o -ath6kl-y += node.o ath6kl-y += sdio.o ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 5ede3d2f1f2a..b32843779c5f 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -626,55 +626,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, ar->sme_state = SME_DISCONNECTED; } -static inline bool is_ch_11a(u16 ch) -{ - return (!((ch >= 2412) && (ch <= 2484))); -} - -/* struct ath6kl_node_table::nt_nodelock is locked when calling this */ -void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni) -{ - struct ieee80211_mgmt *mgmt; - struct ieee80211_channel *channel; - struct ieee80211_supported_band *band; - struct ath6kl_common_ie *cie; - s32 signal; - int freq; - - cie = &ni->ni_cie; - - if (is_ch_11a(cie->ie_chan)) - band = wiphy->bands[IEEE80211_BAND_5GHZ]; /* 11a */ - else if ((cie->ie_erp) || (cie->ie_xrates)) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11g */ - else - band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */ - - freq = cie->ie_chan; - channel = ieee80211_get_channel(wiphy, freq); - signal = ni->ni_snr * 100; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, - "%s: bssid %pM ch %d freq %d size %d\n", __func__, - ni->ni_macaddr, channel->hw_value, freq, ni->ni_framelen); - /* - * Both Beacon and Probe Response frames have same payload structure, - * so it is fine to share the parser for both. - */ - if (ni->ni_framelen < 8 + 2 + 2) - return; - mgmt = (struct ieee80211_mgmt *) (ni->ni_buf - - offsetof(struct ieee80211_mgmt, u)); - cfg80211_inform_bss(wiphy, channel, ni->ni_macaddr, - le64_to_cpu(mgmt->u.beacon.timestamp), - le16_to_cpu(mgmt->u.beacon.capab_info), - le16_to_cpu(mgmt->u.beacon.beacon_int), - mgmt->u.beacon.variable, - ni->ni_buf + ni->ni_framelen - - mgmt->u.beacon.variable, - signal, GFP_ATOMIC); -} - static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { @@ -768,9 +719,6 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status) goto out; } - /* Translate data to cfg80211 mgmt format */ - wlan_iterate_nodes(&ar->scan_table, ar->wdev->wiphy); - cfg80211_scan_done(ar->scan_req, false); if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) { diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index 6b0d45642fe3..b92f0e5d2336 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -75,94 +75,11 @@ enum crypto_type { AES_CRYPT = 0x08, }; -#define ATH6KL_NODE_HASHSIZE 32 -/* simple hash is enough for variation of macaddr */ -#define ATH6KL_NODE_HASH(addr) \ - (((const u8 *)(addr))[ETH_ALEN - 1] % \ - ATH6KL_NODE_HASHSIZE) - -/* - * Table of ath6kl_node instances. Each ieee80211com - * has at least one for holding the scan candidates. - * When operating as an access point or in ibss mode there - * is a second table for associated stations or neighbors. - */ -struct ath6kl_node_table { - spinlock_t nt_nodelock; /* on node table */ - struct bss *nt_node_first; /* information of all nodes */ - struct bss *nt_node_last; /* information of all nodes */ - struct bss *nt_hash[ATH6KL_NODE_HASHSIZE]; - const char *nt_name; /* for debugging */ - u32 nt_node_age; /* node aging time */ -}; - -#define WLAN_NODE_INACT_TIMEOUT_MSEC 120000 -#define WLAN_NODE_INACT_CNT 4 - -struct ath6kl_common_ie { - u16 ie_chan; - u8 *ie_tstamp; - u8 *ie_ssid; - u8 *ie_rates; - u8 *ie_xrates; - u8 *ie_country; - u8 *ie_wpa; - u8 *ie_rsn; - u8 *ie_wmm; - u8 *ie_ath; - u16 ie_capInfo; - u16 ie_beaconInt; - u8 *ie_tim; - u8 *ie_chswitch; - u8 ie_erp; - u8 *ie_wsc; - u8 *ie_htcap; - u8 *ie_htop; -}; - -struct bss { - u8 ni_macaddr[ETH_ALEN]; - u8 ni_snr; - s16 ni_rssi; - struct bss *ni_list_next; - struct bss *ni_list_prev; - struct bss *ni_hash_next; - struct bss *ni_hash_prev; - struct ath6kl_common_ie ni_cie; - u8 *ni_buf; - u16 ni_framelen; - struct ath6kl_node_table *ni_table; - u32 ni_refcnt; - - u32 ni_tstamp; - u32 ni_actcnt; -}; - struct htc_endpoint_credit_dist; struct ath6kl; enum htc_credit_dist_reason; struct htc_credit_state_info; -struct bss *wlan_node_alloc(int wh_size); -void wlan_node_free(struct bss *ni); -void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni, - const u8 *mac_addr); -struct bss *wlan_find_node(struct ath6kl_node_table *nt, - const u8 *mac_addr); -void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni); -void wlan_free_allnodes(struct ath6kl_node_table *nt); -void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg); - -void wlan_node_table_init(struct ath6kl_node_table *nt); -void wlan_node_table_cleanup(struct ath6kl_node_table *nt); - -void wlan_refresh_inactive_nodes(struct ath6kl *ar); - -struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 *ssid, - u32 ssid_len, bool is_wpa2, bool match_ssid); - -void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni); - int ath6k_setup_credit_dist(void *htc_handle, struct htc_credit_state_info *cred_info); void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a9b3b17ef3ef..9e6abb85fc50 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -503,7 +503,6 @@ struct ath6kl { struct workqueue_struct *ath6kl_wq; - struct ath6kl_node_table scan_table; struct dentry *debugfs_phy; u32 send_action_id; @@ -626,5 +625,4 @@ void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no, void ath6kl_wakeup_event(void *dev); void ath6kl_target_failure(struct ath6kl *ar); -void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni); #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 1834e9af5799..7e10f712ae4d 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1418,8 +1418,6 @@ static int ath6kl_init(struct net_device *dev) ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); - wlan_node_table_init(&ar->scan_table); - /* * The reason we have to wait for the target here is that the * driver layer has to init BMI in order to set the host block @@ -1501,7 +1499,6 @@ err_rxbuf_cleanup: err_cleanup_scatter: ath6kl_hif_cleanup_scatter(ar); err_node_cleanup: - wlan_node_table_cleanup(&ar->scan_table); ath6kl_wmi_shutdown(ar->wmi); clear_bit(WMI_ENABLED, &ar->flag); ar->wmi = NULL; @@ -1658,8 +1655,6 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister) free_netdev(dev); - wlan_node_table_cleanup(&ar->scan_table); - kfree(ar->fw_board); kfree(ar->fw_otp); kfree(ar->fw); diff --git a/drivers/net/wireless/ath/ath6kl/node.c b/drivers/net/wireless/ath/ath6kl/node.c deleted file mode 100644 index 131205c610b9..000000000000 --- a/drivers/net/wireless/ath/ath6kl/node.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2004-2011 Atheros Communications Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "htc.h" -#include "wmi.h" -#include "debug.h" - -struct bss *wlan_node_alloc(int wh_size) -{ - struct bss *ni; - - ni = kzalloc(sizeof(struct bss), GFP_ATOMIC); - - if ((ni != NULL) && wh_size) { - ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC); - if (ni->ni_buf == NULL) { - kfree(ni); - return NULL; - } - } - - return ni; -} - -void wlan_node_free(struct bss *ni) -{ - kfree(ni->ni_buf); - kfree(ni); -} - -void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni, - const u8 *mac_addr) -{ - int hash; - - memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN); - hash = ATH6KL_NODE_HASH(mac_addr); - ni->ni_refcnt = 1; - - ni->ni_tstamp = jiffies_to_msecs(jiffies); - ni->ni_actcnt = WLAN_NODE_INACT_CNT; - - spin_lock_bh(&nt->nt_nodelock); - - /* insert at the end of the node list */ - ni->ni_list_next = NULL; - ni->ni_list_prev = nt->nt_node_last; - if (nt->nt_node_last != NULL) - nt->nt_node_last->ni_list_next = ni; - - nt->nt_node_last = ni; - if (nt->nt_node_first == NULL) - nt->nt_node_first = ni; - - /* insert into the hash list */ - ni->ni_hash_next = nt->nt_hash[hash]; - if (ni->ni_hash_next != NULL) - nt->nt_hash[hash]->ni_hash_prev = ni; - - ni->ni_hash_prev = NULL; - nt->nt_hash[hash] = ni; - - spin_unlock_bh(&nt->nt_nodelock); -} - -struct bss *wlan_find_node(struct ath6kl_node_table *nt, - const u8 *mac_addr) -{ - struct bss *ni, *found_ni = NULL; - int hash; - - spin_lock_bh(&nt->nt_nodelock); - - hash = ATH6KL_NODE_HASH(mac_addr); - for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) { - if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) { - ni->ni_refcnt++; - found_ni = ni; - break; - } - } - - spin_unlock_bh(&nt->nt_nodelock); - - return found_ni; -} - -void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni) -{ - int hash; - - spin_lock_bh(&nt->nt_nodelock); - - if (ni->ni_list_prev == NULL) - /* fix list head */ - nt->nt_node_first = ni->ni_list_next; - else - ni->ni_list_prev->ni_list_next = ni->ni_list_next; - - if (ni->ni_list_next == NULL) - /* fix list tail */ - nt->nt_node_last = ni->ni_list_prev; - else - ni->ni_list_next->ni_list_prev = ni->ni_list_prev; - - if (ni->ni_hash_prev == NULL) { - /* first in list so fix the list head */ - hash = ATH6KL_NODE_HASH(ni->ni_macaddr); - nt->nt_hash[hash] = ni->ni_hash_next; - } else { - ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next; - } - - if (ni->ni_hash_next != NULL) - ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev; - - wlan_node_free(ni); - - spin_unlock_bh(&nt->nt_nodelock); -} - -static void wlan_node_dec_free(struct bss *ni) -{ - if ((ni->ni_refcnt--) == 1) - wlan_node_free(ni); -} - -void wlan_free_allnodes(struct ath6kl_node_table *nt) -{ - struct bss *ni; - - while ((ni = nt->nt_node_first) != NULL) - wlan_node_reclaim(nt, ni); -} - -void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg) -{ - struct bss *ni; - - spin_lock_bh(&nt->nt_nodelock); - for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { - ni->ni_refcnt++; - ath6kl_cfg80211_scan_node(arg, ni); - wlan_node_dec_free(ni); - } - spin_unlock_bh(&nt->nt_nodelock); -} - -void wlan_node_table_init(struct ath6kl_node_table *nt) -{ - ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n", - (unsigned long)nt); - - memset(nt, 0, sizeof(struct ath6kl_node_table)); - - spin_lock_init(&nt->nt_nodelock); - - nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC; -} - -void wlan_refresh_inactive_nodes(struct ath6kl *ar) -{ - struct ath6kl_node_table *nt = &ar->scan_table; - struct bss *bss; - u32 now; - - now = jiffies_to_msecs(jiffies); - bss = nt->nt_node_first; - while (bss != NULL) { - /* refresh all nodes except the current bss */ - if (memcmp(ar->bssid, bss->ni_macaddr, ETH_ALEN) != 0) { - if (((now - bss->ni_tstamp) > nt->nt_node_age) - || --bss->ni_actcnt == 0) { - wlan_node_reclaim(nt, bss); - } - } - bss = bss->ni_list_next; - } -} - -void wlan_node_table_cleanup(struct ath6kl_node_table *nt) -{ - wlan_free_allnodes(nt); -} - -struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid, - u32 ssid_len, bool is_wpa2, bool match_ssid) -{ - struct bss *ni, *found_ni = NULL; - u8 *ie_ssid; - - spin_lock_bh(&nt->nt_nodelock); - - for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) { - - ie_ssid = ni->ni_cie.ie_ssid; - - if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) && - (memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) { - - if (match_ssid || - (is_wpa2 && ni->ni_cie.ie_rsn != NULL) || - (!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) { - ni->ni_refcnt++; - found_ni = ni; - break; - } - } - } - - spin_unlock_bh(&nt->nt_nodelock); - - return found_ni; -} - -void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni) -{ - spin_lock_bh(&nt->nt_nodelock); - wlan_node_dec_free(ni); - spin_unlock_bh(&nt->nt_nodelock); -} diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index ff13e0bc646b..3ade9a17c0eb 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2457,43 +2457,6 @@ s32 ath6kl_wmi_get_rate(s8 rate_index) return wmi_rate_tbl[(u32) rate_index][0]; } -void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss) -{ - if (bss) - wlan_node_return(&wmi->parent_dev->scan_table, bss); -} - -struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 * ssid, - u32 ssid_len, bool is_wpa2, - bool match_ssid) -{ - struct bss *node = NULL; - - node = wlan_find_ssid_node(&wmi->parent_dev->scan_table, ssid, - ssid_len, is_wpa2, match_ssid); - return node; -} - -struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 * mac_addr) -{ - struct bss *ni = NULL; - - ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr); - - return ni; -} - -void ath6kl_wmi_node_free(struct wmi *wmi, const u8 * mac_addr) -{ - struct bss *ni = NULL; - - ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr); - if (ni != NULL) - wlan_node_reclaim(&wmi->parent_dev->scan_table, ni); - - return; -} - static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, u32 len) { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index f036e78522ab..f65bc0d6dbef 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2181,8 +2181,6 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb, u8 *ac); int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb); -struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 *mac_addr); -void ath6kl_wmi_node_free(struct wmi *wmi, const u8 *mac_addr); int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb, enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag); @@ -2253,12 +2251,6 @@ s32 ath6kl_wmi_get_rate(s8 rate_index); int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); -struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid, - u32 ssid_len, bool is_wpa2, - bool match_ssid); - -void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss); - /* AP mode */ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p); From 82e14f56f7408cb13c47eef9fd6922f22e88109a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:05 +0300 Subject: [PATCH 081/155] ath6kl: Remove unnecessary bssinfo event header conversion There is no point in unconditionally converting the bssinfo header to the old version since only the new header is being used and the driver can as well read the values from it when needed. Leaving out the conversion saves some extra memory copying. In addition, use the calculated "rssi" value snr - 95 dBm to get the proper value in cfg80211 BSS table (i.e., something that more or less matches with the value used in STA info). Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 37 +++++++-------------------- drivers/net/wireless/ath/ath6kl/wmi.h | 25 +++--------------- 2 files changed, 12 insertions(+), 50 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 3ade9a17c0eb..72cf78c1ca6a 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -381,25 +381,6 @@ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb) return 0; } -static void ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(struct sk_buff *skb, - u8 *datap) -{ - struct wmi_bss_info_hdr2 bih2; - struct wmi_bss_info_hdr *bih; - - memcpy(&bih2, datap, sizeof(struct wmi_bss_info_hdr2)); - - skb_push(skb, 4); - bih = (struct wmi_bss_info_hdr *) skb->data; - - bih->ch = bih2.ch; - bih->frame_type = bih2.frame_type; - bih->snr = bih2.snr; - bih->rssi = a_cpu_to_sle16(bih2.snr - 95); - bih->ie_mask = cpu_to_le32(le16_to_cpu(bih2.ie_mask)); - memcpy(bih->bssid, bih2.bssid, ETH_ALEN); -} - static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) { struct tx_complete_msg_v1 *msg_v1; @@ -912,24 +893,24 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len) static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) { - struct wmi_bss_info_hdr *bih; + struct wmi_bss_info_hdr2 *bih; u8 *buf; struct ieee80211_channel *channel; struct ath6kl *ar = wmi->parent_dev; struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss; - if (len <= sizeof(struct wmi_bss_info_hdr)) + if (len <= sizeof(struct wmi_bss_info_hdr2)) return -EINVAL; - bih = (struct wmi_bss_info_hdr *) datap; - buf = datap + sizeof(struct wmi_bss_info_hdr); - len -= sizeof(struct wmi_bss_info_hdr); + bih = (struct wmi_bss_info_hdr2 *) datap; + buf = datap + sizeof(struct wmi_bss_info_hdr2); + len -= sizeof(struct wmi_bss_info_hdr2); ath6kl_dbg(ATH6KL_DBG_WMI, "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" " "frame_type=%d\n", - bih->ch, bih->snr, a_sle16_to_cpu(bih->rssi), bih->bssid, + bih->ch, bih->snr, bih->snr - 95, bih->bssid, bih->frame_type); if (bih->frame_type != BEACON_FTYPE && @@ -973,7 +954,8 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) memcpy(&mgmt->u.beacon, buf, len); bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt, - 24 + len, bih->snr * 100, GFP_ATOMIC); + 24 + len, (bih->snr - 95) * 100, + GFP_ATOMIC); kfree(mgmt); if (bss == NULL) return -ENOMEM; @@ -2859,8 +2841,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_BSSINFO_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n"); - ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(skb, datap); - ret = ath6kl_wmi_bssinfo_event_rx(wmi, skb->data, skb->len); + ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len); break; case WMI_REGDOMAIN_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n"); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index f65bc0d6dbef..d458d6d3a27f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1393,33 +1393,14 @@ struct roam_ctrl_cmd { u8 roam_ctrl; } __packed; -struct wmi_bss_info_hdr { - __le16 ch; - - /* see, enum wmi_bi_ftype */ - u8 frame_type; - - u8 snr; - a_sle16 rssi; - u8 bssid[ETH_ALEN]; - __le32 ie_mask; -} __packed; - -/* - * BSS INFO HDR version 2.0 - * With 6 bytes HTC header and 6 bytes of WMI header - * WMI_BSS_INFO_HDR cannot be accommodated in the removed 802.11 management - * header space. - * - Reduce the ie_mask to 2 bytes as only two bit flags are used - * - Remove rssi and compute it on the host. rssi = snr - 95 - */ +/* BSS INFO HDR version 2.0 */ struct wmi_bss_info_hdr2 { - __le16 ch; + __le16 ch; /* frequency in MHz */ /* see, enum wmi_bi_ftype */ u8 frame_type; - u8 snr; + u8 snr; /* note: rssi = snr - 95 dBm */ u8 bssid[ETH_ALEN]; __le16 ie_mask; } __packed; From 551185ca0a97a11917edc3ad8e11d68912795902 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:06 +0300 Subject: [PATCH 082/155] ath6kl: Update BSS information after connection Since we may end up using a dummy BSS entry when roaming, allow one Beacon frame -based bssinfo from the current BSS to be processed prior to starting to filter all bssinfo events. This allows cfg80211 BSS table to be filled with proper data in the roaming case where the full Beacon data may not have been present at the time of roamed event. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 ++ drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/main.c | 10 +++++++--- drivers/net/wireless/ath/ath6kl/wmi.c | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b32843779c5f..0bdd837d6121 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -360,6 +360,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, } if (!ar->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) { ath6kl_err("couldn't set bss filtering\n"); up(&ar->sem); @@ -638,6 +639,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return -EIO; if (!ar->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); ret = ath6kl_wmi_bssfilter_cmd( ar->wmi, (test_bit(CONNECTED, &ar->flag) ? diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 9e6abb85fc50..c14bb75d3614 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -394,6 +394,7 @@ struct ath6kl_req_key { #define SKIP_SCAN 11 #define WLAN_ENABLED 12 #define TESTMODE 13 +#define CLEAR_BSSFILTER_ON_BEACON 14 struct ath6kl { struct device *dev; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 55d3331bed85..30b5a53db9ed 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1011,8 +1011,10 @@ void ath6kl_scan_complete_evt(struct ath6kl *ar, int status) { ath6kl_cfg80211_scan_complete_event(ar, status); - if (!ar->usr_bss_filter) + if (!ar->usr_bss_filter) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); + } ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status); } @@ -1056,8 +1058,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid, ar->next_ep_id = ENDPOINT_2; } - if (!ar->usr_bss_filter) - ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); + if (!ar->usr_bss_filter) { + set_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); + ath6kl_wmi_bssfilter_cmd(ar->wmi, CURRENT_BSS_FILTER, 0); + } } void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 72cf78c1ca6a..f7dcb56ab354 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -917,6 +917,12 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) bih->frame_type != PROBERESP_FTYPE) return 0; /* Only update BSS table for now */ + if (bih->frame_type == BEACON_FTYPE && + test_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag)) { + clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); + ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0); + } + channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch)); if (channel == NULL) return -EINVAL; From 32c1087460626f9cfa2b397eafd247bf039bacac Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Sep 2011 19:15:07 +0300 Subject: [PATCH 083/155] ath6kl: Export beacon interval and DTIM period through STA info Now that we allow the first Beacon frame after each connection to be processed at the host, we can figure out the DTIM period and expose it with Beacon interval through STA info BSS parameters to user space. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/wmi.c | 11 +++++++++++ 3 files changed, 30 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0bdd837d6121..c3540bbfcac6 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -483,6 +483,13 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel, assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset; + /* + * Store Beacon interval here; DTIM period will be available only once + * a Beacon frame from the AP is seen. + */ + ar->assoc_bss_beacon_int = beacon_intvl; + clear_bit(DTIM_PERIOD_AVAIL, &ar->flag); + if (nw_type & ADHOC_NETWORK) { if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, @@ -1366,6 +1373,15 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, sinfo->filled |= STATION_INFO_TX_BITRATE; + if (test_bit(CONNECTED, &ar->flag) && + test_bit(DTIM_PERIOD_AVAIL, &ar->flag) && + ar->nw_type == INFRA_NETWORK) { + sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->bss_param.flags = 0; + sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period; + sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int; + } + return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index c14bb75d3614..82be42f5edc8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -395,6 +395,7 @@ struct ath6kl_req_key { #define WLAN_ENABLED 12 #define TESTMODE 13 #define CLEAR_BSSFILTER_ON_BEACON 14 +#define DTIM_PERIOD_AVAIL 15 struct ath6kl { struct device *dev; @@ -511,6 +512,8 @@ struct ath6kl { u16 next_chan; bool p2p; + u16 assoc_bss_beacon_int; + u8 assoc_bss_dtim_period; #ifdef CONFIG_ATH6KL_DEBUG struct { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index f7dcb56ab354..b90d116c018c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -930,6 +930,17 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len) if (len < 8 + 2 + 2) return -EINVAL; + if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &ar->flag) && + memcmp(bih->bssid, ar->bssid, ETH_ALEN) == 0) { + const u8 *tim; + tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2, + len - 8 - 2 - 2); + if (tim && tim[1] >= 2) { + ar->assoc_bss_dtim_period = tim[3]; + set_bit(DTIM_PERIOD_AVAIL, &ar->flag); + } + } + /* * In theory, use of cfg80211_inform_bss() would be more natural here * since we do not have the full frame. However, at least for now, From 011a36e1193c02abcdc4853be09275a0fe9d1a32 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Mon, 19 Sep 2011 13:29:16 +0530 Subject: [PATCH 084/155] ath6kl: Indicate the roaming capability of the firmware When the rssi of the current AP drops, both wpa_supplicant and the firmware may do a background scan to find a better AP and try to associate. This might lead to a race condition where both may try to connect to some AP based on their scan results. Since the firmware is capable of handling roaming, let wpa_supplicant know about this capability so that it will back off from bgscan based roaming. Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 7e10f712ae4d..80c532d7f46d 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1487,6 +1487,8 @@ static int ath6kl_init(struct net_device *dev) ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; + ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + status = ath6kl_target_config_wlan_params(ar); if (!status) goto ath6kl_init_done; From 865121361f0be55555c540c3df444ed06e090b33 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 21 Sep 2011 16:57:29 +0300 Subject: [PATCH 085/155] ath6kl: Report PMKSA candidate events through cfg80211 This allows RSN pre-authentication to be used when roaming decisions are done in the target. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 30 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 10 +++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index b90d116c018c..47fbb8e7686b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1097,6 +1097,35 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len) return 0; } +static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmi_neighbor_report_event *ev; + u8 i; + + if (len < sizeof(*ev)) + return -EINVAL; + ev = (struct wmi_neighbor_report_event *) datap; + if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) + > len) { + ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event " + "(num=%d len=%d)\n", ev->num_neighbors, len); + return -EINVAL; + } + for (i = 0; i < ev->num_neighbors; i++) { + ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n", + i + 1, ev->num_neighbors, ev->neighbor[i].bssid, + ev->neighbor[i].bss_flags); + cfg80211_pmksa_candidate_notify(wmi->parent_dev->net_dev, i, + ev->neighbor[i].bssid, + !!(ev->neighbor[i].bss_flags & + WMI_PREAUTH_CAPABLE_BSS), + GFP_ATOMIC); + } + + return 0; +} + /* * Target is reporting a programming error. This is for * developer aid only. Target only checks a few common violations @@ -2870,6 +2899,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) break; case WMI_NEIGHBOR_REPORT_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n"); + ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len); break; case WMI_SCAN_COMPLETE_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n"); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index d458d6d3a27f..f8e644d54aa7 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1439,6 +1439,16 @@ enum wmi_bss_flags { WMI_PMKID_VALID_BSS = 0x02, }; +struct wmi_neighbor_info { + u8 bssid[ETH_ALEN]; + u8 bss_flags; /* enum wmi_bss_flags */ +} __packed; + +struct wmi_neighbor_report_event { + u8 num_neighbors; + struct wmi_neighbor_info neighbor[0]; +} __packed; + /* TKIP MIC Error Event */ struct wmi_tkip_micerr_event { u8 key_id; From 5694f962964c5162f6b49ddb5d517180bd7d1d98 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 19 Sep 2011 21:38:44 +0300 Subject: [PATCH 086/155] ath6kl: pass only unicast frames for aggregation When pinging form ar6003 to the AP RTT was high even when power save was disabled: 100 packets transmitted, 97 received, 3% packet loss, time 99125ms rtt min/avg/max/mdev = 1.875/46.733/795.506/139.181 ms After some investigation one reason for this was that received multicast traffic confused the aggrecation logic and caused 400 ms timeouts when receiving multicast frames from AP. A simple way to fix is to pass only unicast frames for aggregation. This improves RTT: 100 packets transmitted, 99 received, 1% packet loss, time 99144ms rtt min/avg/max/mdev = 2.083/13.084/403.390/56.794 ms Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index fffd92920d35..348c6463fe00 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1230,9 +1230,15 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) ath6kl_data_tx(skb1, ar->net_dev); } - if (!aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no, - is_amsdu, skb)) - ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb); + datap = (struct ethhdr *) skb->data; + + if (is_unicast_ether_addr(datap->h_dest) && + aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no, + is_amsdu, skb)) + /* aggregation code will handle the skb */ + return; + + ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb); } static void aggr_timeout(unsigned long arg) @@ -1249,10 +1255,6 @@ static void aggr_timeout(unsigned long arg) if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress) continue; - /* - * FIXME: these timeouts happen quite fruently, something - * line once within 60 seconds. Investigate why. - */ stats->num_timeouts++; ath6kl_dbg(ATH6KL_DBG_AGGR, "aggr timeout (st %d end %d)\n", From 1de547d6dcc66f6d9d227de9f510acbbf88a654f Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 23 Sep 2011 10:57:50 +0530 Subject: [PATCH 087/155] ath6kl: Fix disconnect event reporting Driver does not report disconnect event properly when in connecting state, this leads to issues failures in starting reconnection. Send a disconnect command to target when a disconnect event is received with reason code other than 3 (DISCONNECT_CMD - disconnect request from host) to make the frimware stop trying to connect even after giving disconnect event. There will be one more disconnect event for this disconnect command with reason code DISCONNECT_CMD which will be notified to cfg80211. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c3540bbfcac6..c84f53d6523b 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -602,22 +602,20 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason, } } - if (!test_bit(CONNECT_PEND, &ar->flag)) { - if (reason != DISCONNECT_CMD) - ath6kl_wmi_disconnect_cmd(ar->wmi); + /* + * Send a disconnect command to target when a disconnect event is + * received with reason code other than 3 (DISCONNECT_CMD - disconnect + * request from host) to make the firmware stop trying to connect even + * after giving disconnect event. There will be one more disconnect + * event for this disconnect command with reason code DISCONNECT_CMD + * which will be notified to cfg80211. + */ - return; - } - - if (reason == NO_NETWORK_AVAIL) { - /* connect cmd failed */ + if (reason != DISCONNECT_CMD) { ath6kl_wmi_disconnect_cmd(ar->wmi); return; } - if (reason != DISCONNECT_CMD) - return; - clear_bit(CONNECT_PEND, &ar->flag); if (ar->sme_state == SME_CONNECTING) { From 3038fac8d8dbecbda8fe92eb94bf1992e6b60ee4 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 26 Sep 2011 14:29:18 +0530 Subject: [PATCH 088/155] ath6kl: Fix compilation error while compiling w/o debug drivers/net/wireless/ath/ath6kl/htc_hif.o: In function `ath6kl_debug_fwlog_event': drivers/net/wireless/ath/ath6kl/debug.h:109: multiple definition of `ath6kl_debug_fwlog_event' drivers/net/wireless/ath/ath6kl/debug.o: drivers/net/wireless/ath/ath6kl/debug.h:109: first defined here drivers/net/wireless/ath/ath6kl/htc_hif.o: In function `ath6kl_debug_cleanup': drivers/net/wireless/ath/ath6kl/debug.h:118: multiple definition of `ath6kl_debug_cleanup' drivers/net/wireless/ath/ath6kl/debug.o: drivers/net/wireless/ath/ath6kl/debug.h:118: first defined here Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index f0d64711b410..89bf8e1138a3 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -105,7 +105,8 @@ static inline void dump_cred_dist_stats(struct htc_target *target) { } -void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) +static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar, + const void *buf, size_t len) { } @@ -114,7 +115,7 @@ static inline int ath6kl_debug_init(struct ath6kl *ar) return 0; } -void ath6kl_debug_cleanup(struct ath6kl *ar) +static inline void ath6kl_debug_cleanup(struct ath6kl *ar) { } From aad9339fa2a5e5b51874cfec9883819f59090198 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 26 Sep 2011 14:49:03 +0530 Subject: [PATCH 089/155] ath6kl: Remove unnecessary retrieval of first list entry in ath6kl_htc_tx_setup_scat_list() It is unnecessary to take the first list entry from queue again for transmission. Sometimes it may look racy when the head of the list changes between subsequent retrival, but should not happen in practical. Reported-by: Jouni Malinen Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index 9aa2e4447900..feed98535c9f 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -389,7 +389,6 @@ static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, rem_scat -= len; /* now remove it from the queue */ - packet = list_first_entry(queue, struct htc_packet, list); list_del(&packet->list); scat_req->scat_list[i].packet = packet; From 00b1edf16960695d820607845797b14e6ed1a26c Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 27 Sep 2011 11:00:08 +0300 Subject: [PATCH 090/155] ath6kl: fix TCP corruption Commit 94e532d1a ("ath6kl: Fix system freeze under heavy data load") aligns the skb data without checking if the skb is cloned. Because of this ath6kl can corrupt the local TCP stack information that can result in TCP retransmission failing and TCP connections stalling. To avoid the corruption we need to copy the skb. Now the alignment in ath6kl_htc_tx_buf_align() doesn't corrupt TCP packets anymore (and is not even used for the cloned skb's that got copied since the alignment of the data is handled at the copy time). Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/txrx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 348c6463fe00..0869ff396b57 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -317,6 +317,24 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) spin_unlock_bh(&ar->lock); + if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) && + skb_cloned(skb)) { + /* + * We will touch (move the buffer data to align it. Since the + * skb buffer is cloned and not only the header is changed, we + * have to copy it to allow the changes. Since we are copying + * the data here, we may as well align it by reserving suitable + * headroom to avoid the memmove in ath6kl_htc_tx_buf_align(). + */ + struct sk_buff *nskb; + + nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC); + if (nskb == NULL) + goto fail_tx; + kfree_skb(skb); + skb = nskb; + } + cookie->skb = skb; cookie->map_no = map_no; set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, From 1b4304da0adcc31727da3ee7f89dd180f4e65473 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 11:05:26 +0300 Subject: [PATCH 091/155] ath6kl: allow firmware to override firmware patch address In some firmware versions their patch address has changed. If the firmware provides one, use it to override the default address. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 82be42f5edc8..9ecf22bd4fc9 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -69,6 +69,7 @@ enum ath6kl_fw_ie_type { ATH6KL_FW_IE_PATCH_IMAGE = 4, ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5, ATH6KL_FW_IE_CAPABILITIES = 6, + ATH6KL_FW_IE_PATCH_ADDR = 7, }; enum ath6kl_fw_capability { diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 80c532d7f46d..e9785feeea17 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1000,6 +1000,13 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) __set_bit(i, ar->fw_capabilities); } break; + case ATH6KL_FW_IE_PATCH_ADDR: + if (ie_len != sizeof(*val)) + break; + + val = (__le32 *) data; + ar->hw.dataset_patch_addr = le32_to_cpup(val); + break; default: ath6kl_dbg(ATH6KL_DBG_TRC, "Unknown fw ie: %u\n", le32_to_cpup(&hdr->id)); From 9fd481e03c1e9c76c814b88b9ea1cbda9afb0812 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 14 Jul 2011 08:48:32 -0400 Subject: [PATCH 092/155] Bluetooth: Allow ACL packets over USB in HCI_RAW mode Removed tests which prevent transmission of ACL packets when the device is in HCI_RAW mode. These tests verified that there are ACL or LE links currently tracked by the HCI connection manager. However, a HCI_RAW mode device does not use the connection manager. In these circumstances, the connection counts will be zero, and thus, transmitted ACL packets dropped. The acl_num test is actually a vestige of a previous bulk URB scheme that is no longer used by this driver (bulk URBs were not started until at least one ACL connection was created). This was incompatible with some endpoint implementations and was dropped - see commit 43c2e57f94. The utility of these tests is marginal - currently, the hci tx scheduler cannot send an ACL or LE packet for an untracked connection (except if the device is in HCI_RAW mode). Lastly, no other transport layer driver enforces these same tests. Signed-off-by: Peter Hurley Acked-by: Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btusb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9cbac6b445e1..2755c1a9c38e 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -708,8 +708,7 @@ static int btusb_send_frame(struct sk_buff *skb) break; case HCI_ACLDATA_PKT: - if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 && - hdev->conn_hash.le_num < 1)) + if (!data->bulk_tx_ep) return -ENODEV; urb = usb_alloc_urb(0, GFP_ATOMIC); From 449357200c5d73d80a9c42dee5dafed684b3cd17 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 22 Jul 2011 14:53:58 -0700 Subject: [PATCH 093/155] Bluetooth: Linearize skbs for use in BNEP, CMTP, HIDP, and RFCOMM Fragmented skbs are only encountered when receiving ERTM or streaming mode L2CAP data. BNEP, CMTP, HIDP, and RFCOMM generally use basic mode, but they need to handle fragments without crashing. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/bnep/core.c | 5 ++++- net/bluetooth/cmtp/core.c | 5 ++++- net/bluetooth/hidp/core.c | 10 ++++++++-- net/bluetooth/rfcomm/core.c | 5 ++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d9edfe8bf9d6..91bcd3a961ec 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -492,7 +492,10 @@ static int bnep_session(void *arg) /* RX */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - bnep_rx_frame(s, skb); + if (!skb_linearize(skb)) + bnep_rx_frame(s, skb); + else + kfree_skb(skb); } if (sk->sk_state != BT_CONNECTED) diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 521baa4fe835..7d00ddf9e9dc 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -302,7 +302,10 @@ static int cmtp_session(void *arg) while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - cmtp_recv_frame(session, skb); + if (!skb_linearize(skb)) + cmtp_recv_frame(session, skb); + else + kfree_skb(skb); } cmtp_process_transmit(session); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b83979c548b2..075a3e920caf 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -716,12 +716,18 @@ static int hidp_session(void *arg) while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_ctrl_frame(session, skb); + if (!skb_linearize(skb)) + hidp_recv_ctrl_frame(session, skb); + else + kfree_skb(skb); } while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_intr_frame(session, skb); + if (!skb_linearize(skb)) + hidp_recv_intr_frame(session, skb); + else + kfree_skb(skb); } hidp_process_transmit(session); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 5ba3f6df665c..38b618c96de6 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1853,7 +1853,10 @@ static inline void rfcomm_process_rx(struct rfcomm_session *s) /* Get data directly from socket receive queue without copying it. */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - rfcomm_recv_frame(s, skb); + if (!skb_linearize(skb)) + rfcomm_recv_frame(s, skb); + else + kfree_skb(skb); } if (sk->sk_state == BT_CLOSED) { From 5b668eb3270f3f9c13ddf6e4fb57bf20c83dccff Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 22 Jul 2011 14:53:59 -0700 Subject: [PATCH 094/155] Bluetooth: Handle fragmented skbs in bt_sock_stream_recvmsg() ERTM reassembly will be more efficient when skbs are linked together rather than copying every incoming data byte. The existing stream recv function assumes skbs are linear, so it needs to know how to handle fragments before reassembly is changed. bt_sock_recvmsg() already handles fragmented skbs. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/af_bluetooth.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 117e0d161780..062124cd89cf 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -349,7 +349,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, } chunk = min_t(unsigned int, skb->len, size); - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (!copied) copied = -EFAULT; @@ -361,7 +361,33 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, sock_recv_ts_and_drops(msg, sk, skb); if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); + int skb_len = skb_headlen(skb); + + if (chunk <= skb_len) { + __skb_pull(skb, chunk); + } else { + struct sk_buff *frag; + + __skb_pull(skb, skb_len); + chunk -= skb_len; + + skb_walk_frags(skb, frag) { + if (chunk <= frag->len) { + /* Pulling partial data */ + skb->len -= chunk; + skb->data_len -= chunk; + __skb_pull(frag, chunk); + break; + } else if (frag->len) { + /* Pulling all frag data */ + chunk -= frag->len; + skb->len -= frag->len; + skb->data_len -= frag->len; + __skb_pull(frag, frag->len); + } + } + } + if (skb->len) { skb_queue_head(&sk->sk_receive_queue, skb); break; From 84084a3197a9fdec10fa542c0df11928a784e7fc Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 22 Jul 2011 14:54:00 -0700 Subject: [PATCH 095/155] Bluetooth: Perform L2CAP SDU reassembly without copying data Use sk_buff fragment capabilities to link together incoming skbs instead of allocating a new skb for reassembly and copying. The new reassembly code works equally well for ERTM and streaming mode, so there is now one reassembly function instead of two. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 +- net/bluetooth/l2cap_core.c | 245 +++++++++++----------------------- 2 files changed, 82 insertions(+), 166 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7f878b9d5642..ab90ae0970a6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -354,8 +354,8 @@ struct l2cap_chan { __u8 retry_count; __u8 num_acked; __u16 sdu_len; - __u16 partial_sdu_len; struct sk_buff *sdu; + struct sk_buff *sdu_last_frag; __u8 remote_tx_win; __u8 remote_max_tx; @@ -448,7 +448,6 @@ enum { #define L2CAP_CONF_MAX_CONF_RSP 2 enum { - CONN_SAR_SDU, CONN_SREJ_SENT, CONN_WAIT_F, CONN_SREJ_ACT, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1611b3544bb1..12b1e742d706 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3128,102 +3128,104 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, return 0; } -static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +static void append_skb_frag(struct sk_buff *skb, + struct sk_buff *new_frag, struct sk_buff **last_frag) { - struct sk_buff *_skb; - int err; + /* skb->len reflects data in skb as well as all fragments + * skb->data_len reflects only data in fragments + */ + if (!skb_has_frag_list(skb)) + skb_shinfo(skb)->frag_list = new_frag; + + new_frag->next = NULL; + + (*last_frag)->next = new_frag; + *last_frag = new_frag; + + skb->len += new_frag->len; + skb->data_len += new_frag->len; + skb->truesize += new_frag->truesize; +} + +static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +{ + int err = -EINVAL; switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto drop; + if (chan->sdu) + break; - return chan->ops->recv(chan->data, skb); + err = chan->ops->recv(chan->data, skb); + break; case L2CAP_SDU_START: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto drop; + if (chan->sdu) + break; chan->sdu_len = get_unaligned_le16(skb->data); - - if (chan->sdu_len > chan->imtu) - goto disconnect; - - chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); - if (!chan->sdu) - return -ENOMEM; - - /* pull sdu_len bytes only after alloc, because of Local Busy - * condition we have to be sure that this will be executed - * only once, i.e., when alloc does not fail */ skb_pull(skb, 2); - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); + if (chan->sdu_len > chan->imtu) { + err = -EMSGSIZE; + break; + } - set_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len = skb->len; + if (skb->len >= chan->sdu_len) + break; + + chan->sdu = skb; + chan->sdu_last_frag = skb; + + skb = NULL; + err = 0; break; case L2CAP_SDU_CONTINUE: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto disconnect; - if (!chan->sdu) - goto disconnect; + break; - chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->sdu_len) - goto drop; + append_skb_frag(chan->sdu, skb, + &chan->sdu_last_frag); + skb = NULL; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); + if (chan->sdu->len >= chan->sdu_len) + break; + err = 0; break; case L2CAP_SDU_END: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto disconnect; - if (!chan->sdu) - goto disconnect; + break; - chan->partial_sdu_len += skb->len; + append_skb_frag(chan->sdu, skb, + &chan->sdu_last_frag); + skb = NULL; - if (chan->partial_sdu_len > chan->imtu) - goto drop; + if (chan->sdu->len != chan->sdu_len) + break; - if (chan->partial_sdu_len != chan->sdu_len) - goto drop; + err = chan->ops->recv(chan->data, chan->sdu); - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - _skb = skb_clone(chan->sdu, GFP_ATOMIC); - if (!_skb) { - return -ENOMEM; + if (!err) { + /* Reassembly complete */ + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; } - - err = chan->ops->recv(chan->data, _skb); - if (err < 0) { - kfree_skb(_skb); - return err; - } - - clear_bit(CONN_SAR_SDU, &chan->conn_state); - - kfree_skb(chan->sdu); break; } - kfree_skb(skb); - return 0; + if (err) { + kfree_skb(skb); + kfree_skb(chan->sdu); + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; + } -drop: - kfree_skb(chan->sdu); - chan->sdu = NULL; - -disconnect: - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - kfree_skb(skb); - return 0; + return err; } static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) @@ -3277,99 +3279,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) } } -static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) -{ - struct sk_buff *_skb; - int err = -EINVAL; - - /* - * TODO: We have to notify the userland if some data is lost with the - * Streaming Mode. - */ - - switch (control & L2CAP_CTRL_SAR) { - case L2CAP_SDU_UNSEGMENTED: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { - kfree_skb(chan->sdu); - break; - } - - err = chan->ops->recv(chan->data, skb); - if (!err) - return 0; - - break; - - case L2CAP_SDU_START: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { - kfree_skb(chan->sdu); - break; - } - - chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, 2); - - if (chan->sdu_len > chan->imtu) { - err = -EMSGSIZE; - break; - } - - chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); - if (!chan->sdu) { - err = -ENOMEM; - break; - } - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - set_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len = skb->len; - err = 0; - break; - - case L2CAP_SDU_CONTINUE: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - break; - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->sdu_len) - kfree_skb(chan->sdu); - else - err = 0; - - break; - - case L2CAP_SDU_END: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - break; - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - clear_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len += skb->len; - - if (chan->partial_sdu_len > chan->imtu) - goto drop; - - if (chan->partial_sdu_len == chan->sdu_len) { - _skb = skb_clone(chan->sdu, GFP_ATOMIC); - err = chan->ops->recv(chan->data, _skb); - if (err < 0) - kfree_skb(_skb); - } - err = 0; - -drop: - kfree_skb(chan->sdu); - break; - } - - kfree_skb(skb); - return err; -} - static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) { struct sk_buff *skb; @@ -3384,7 +3293,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) skb = skb_dequeue(&chan->srej_q); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(chan, skb, control); + err = l2cap_reassemble_sdu(chan, skb, control); if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3544,7 +3453,7 @@ expected: return 0; } - err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control); + err = l2cap_reassemble_sdu(chan, skb, rx_control); chan->buffer_seq = (chan->buffer_seq + 1) % 64; if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3860,12 +3769,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk tx_seq = __get_txseq(control); - if (chan->expected_tx_seq == tx_seq) - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; - else - chan->expected_tx_seq = (tx_seq + 1) % 64; + if (chan->expected_tx_seq != tx_seq) { + /* Frame(s) missing - must discard partial SDU */ + kfree_skb(chan->sdu); + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; - l2cap_streaming_reassembly_sdu(chan, skb, control); + /* TODO: Notify userland of missing data */ + } + + chan->expected_tx_seq = (tx_seq + 1) % 64; + + if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto done; From 0e8339151fa85cb9b088abfb13e2dd5214a25429 Mon Sep 17 00:00:00 2001 From: Anderson Lizardo Date: Wed, 27 Jul 2011 18:40:09 -0300 Subject: [PATCH 096/155] Bluetooth: use recommended LE connection parameters The new connection parameters now match the recommended values for Proximity and Health Thermometer profiles. The previous values were ramdomly chosen, and are either too low or too high for most cases. New values: Scan Interval: 60 ms Scan Window: 30 ms Minimum Connection Interval: 50 ms Maximum Connection Interval: 70 ms Supervision Timeout: 420 ms See "Table 5.2: Recommended Scan Interval and Scan Window Values" and "Table 5.3: Recommended Connection Interval Values" for both profiles for details. Note that the "fast connection" parameters were chosen, because we do not support yet dynamically changing these parameters from initiator side. Additionally, the Proximity profile recommends (section "4.4 Alert on Link Loss"): "It is recommended that the Link Supervision Timeout (LSTO) is set to 6x the connection interval." Minimum_CE_Length and Maximum_CE_Length were also changed from 0x0001 to 0x0000 because they are informational and optional, and old value was not reflecting reality. Signed-off-by: Anderson Lizardo Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c2df7bf1d374..c1c597e3e198 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -56,15 +56,15 @@ static void hci_le_connect(struct hci_conn *conn) conn->sec_level = BT_SECURITY_LOW; memset(&cp, 0, sizeof(cp)); - cp.scan_interval = cpu_to_le16(0x0004); - cp.scan_window = cpu_to_le16(0x0004); + cp.scan_interval = cpu_to_le16(0x0060); + cp.scan_window = cpu_to_le16(0x0030); bacpy(&cp.peer_addr, &conn->dst); cp.peer_addr_type = conn->dst_type; - cp.conn_interval_min = cpu_to_le16(0x0008); - cp.conn_interval_max = cpu_to_le16(0x0100); - cp.supervision_timeout = cpu_to_le16(0x0064); - cp.min_ce_len = cpu_to_le16(0x0001); - cp.max_ce_len = cpu_to_le16(0x0001); + cp.conn_interval_min = cpu_to_le16(0x0028); + cp.conn_interval_max = cpu_to_le16(0x0038); + cp.supervision_timeout = cpu_to_le16(0x002a); + cp.min_ce_len = cpu_to_le16(0x0000); + cp.max_ce_len = cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); } From 67c9e840a098fa62c0b464387160ff8f52a7ef4a Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 28 Jul 2011 16:24:33 +0200 Subject: [PATCH 097/155] Bluetooth: Mark not declared l2cap_core functions as static Signed-off-by: Szymon Janc Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 12b1e742d706..8cd12917733b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1245,7 +1245,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) __clear_retrans_timer(chan); } -void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) +static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { struct hci_conn *hcon = chan->conn->hcon; u16 flags; @@ -1261,7 +1261,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) hci_send_acl(hcon, skb, flags); } -void l2cap_streaming_send(struct l2cap_chan *chan) +static void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; u16 control, fcs; @@ -1327,7 +1327,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) l2cap_do_send(chan, tx_skb); } -int l2cap_ertm_send(struct l2cap_chan *chan) +static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; u16 control, fcs; @@ -1465,7 +1465,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1495,7 +1495,7 @@ struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr return skb; } -struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1572,7 +1572,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, return skb; } -int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; struct sk_buff_head sar_queue; From 9a7308341b71f3c5e88e6a30f9d6a1cfb3bc2b4f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 23:33:28 +0300 Subject: [PATCH 098/155] ath6kl: silence "invalid rate" warning For some reason firmware is sending invalid rates when we try to query current bitrate from ath6kl_get_station() and a warning is issued: [ 3810.415720] ath6kl: invalid rate: 1935633515 [ 3811.105493] ath6kl: invalid rate: 1935633515 [ 3811.556063] ath6kl: invalid rate: 1935633515 As the warning happens way too often, convert the warning to a debug message once we have a proper fix. But to make it easy to follow how often the problem appears, add a debugfs to print various statistics about workarounds and make this issue the first WAR. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 4 +- drivers/net/wireless/ath/ath6kl/core.h | 4 ++ drivers/net/wireless/ath/ath6kl/debug.c | 48 ++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/debug.h | 9 ++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c84f53d6523b..8d9fbd4a62b7 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1365,7 +1365,9 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else { - ath6kl_warn("invalid rate: %d\n", rate); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "invalid rate from stats: %d\n", rate); + ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE); return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 9ecf22bd4fc9..6d8a4845baaf 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -525,6 +525,10 @@ struct ath6kl { unsigned int dbgfs_diag_reg; u32 diag_reg_addr_wr; u32 diag_reg_val_wr; + + struct { + unsigned int invalid_rate; + } war_stats; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ }; diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 4fc83ccbf8bd..5237369cd521 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -192,6 +192,51 @@ static int ath6kl_debugfs_open(struct inode *inode, struct file *file) return 0; } +void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) +{ + switch (war) { + case ATH6KL_WAR_INVALID_RATE: + ar->debug.war_stats.invalid_rate++; + break; + } +} + +static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath6kl *ar = file->private_data; + char *buf; + unsigned int len = 0, buf_len = 1500; + ssize_t ret_cnt; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%25s\n", + "Workaround stats"); + len += scnprintf(buf + len, buf_len - len, "%25s\n\n", + "================="); + len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", + "Invalid rates", ar->debug.war_stats.invalid_rate); + + if (WARN_ON(len > buf_len)) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_war_stats = { + .read = read_file_war_stats, + .open = ath6kl_debugfs_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf, size_t buf_len) { @@ -873,6 +918,9 @@ int ath6kl_debug_init(struct ath6kl *ar) debugfs_create_file("reg_write", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, &fops_diag_reg_write); + debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar, + &fops_war_stats); + return 0; } diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 89bf8e1138a3..91f4bc35f968 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -52,6 +52,10 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...) #define AR_DBG_LVL_CHECK(mask) (debug_mask & mask) +enum ath6kl_war { + ATH6KL_WAR_INVALID_RATE, +}; + #ifdef CONFIG_ATH6KL_DEBUG #define ath6kl_dbg(mask, fmt, ...) \ ({ \ @@ -79,6 +83,7 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_enable_reg *irq_en_reg); void dump_cred_dist_stats(struct htc_target *target); void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len); +void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war); int ath6kl_debug_init(struct ath6kl *ar); void ath6kl_debug_cleanup(struct ath6kl *ar); @@ -110,6 +115,10 @@ static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar, { } +static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) +{ +} + static inline int ath6kl_debug_init(struct ath6kl *ar) { return 0; From ef094103233344271990d15045d6a776386c3784 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 14:30:45 +0300 Subject: [PATCH 099/155] ath6kl: add prefix parameter to ath6kl_dbg_dump() Makes it easier to recognise longs dumps. Obligatory screenshot using "rx" prefix: ath6kl: ath6kl_rx rx 00000000: 10 10 00 00 00 00 08 30 00 00 00 00 00 00 f9 0b .......0........ rx 00000010: 2c 44 08 30 00 00 f9 0b 0c a4 02 00 00 00 73 d2 ,D.0..........s. rx 00000020: 94 00 f9 0b 04 8c 01 00 02 00 07 02 02 00 f9 0b ................ Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 14 +++++++------ drivers/net/wireless/ath/ath6kl/htc.c | 27 +++++++++++++------------ drivers/net/wireless/ath/ath6kl/txrx.c | 6 ++++-- drivers/net/wireless/ath/ath6kl/wmi.c | 3 ++- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 91f4bc35f968..9f906c2d3f65 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -69,12 +69,14 @@ enum ath6kl_war { }) static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, - const char *msg, const void *buf, - size_t len) + const char *msg, const char *prefix, + const void *buf, size_t len) { if (debug_mask & mask) { - ath6kl_dbg(mask, "%s\n", msg); - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); + if (msg) + ath6kl_dbg(mask, "%s\n", msg); + + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); } } @@ -95,8 +97,8 @@ static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, } static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, - const char *msg, const void *buf, - size_t len) + const char *msg, const char *prefix, + const void *buf, size_t len) { } diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc.c index feed98535c9f..f88a7c9e4148 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc.c @@ -1192,9 +1192,9 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) packets->act_len + HTC_HDR_LENGTH); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, - "Unexpected ENDPOINT 0 Message", - packets->buf - HTC_HDR_LENGTH, - packets->act_len + HTC_HDR_LENGTH); + "Unexpected ENDPOINT 0 Message", "", + packets->buf - HTC_HDR_LENGTH, + packets->act_len + HTC_HDR_LENGTH); } htc_reclaim_rxbuf(context, packets, &context->endpoint[0]); @@ -1328,7 +1328,7 @@ static int htc_parse_trailer(struct htc_target *target, memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead", - next_lk_ahds, 4); + "", next_lk_ahds, 4); *n_lk_ahds = 1; } @@ -1347,7 +1347,7 @@ static int htc_parse_trailer(struct htc_target *target, (struct htc_bundle_lkahd_rpt *) record_buf; ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd", - record_buf, record->len); + "", record_buf, record->len); for (i = 0; i < len; i++) { memcpy((u8 *)&next_lk_ahds[i], @@ -1380,7 +1380,8 @@ static int htc_proc_trailer(struct htc_target *target, ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", buf, len); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", "", + buf, len); orig_buf = buf; orig_len = len; @@ -1418,7 +1419,7 @@ static int htc_proc_trailer(struct htc_target *target, if (status) ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer", - orig_buf, orig_len); + "", orig_buf, orig_len); return status; } @@ -1435,8 +1436,8 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target, if (n_lkahds != NULL) *n_lkahds = 0; - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", packet->buf, - packet->act_len); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", "htc ", + packet->buf, packet->act_len); /* * NOTE: we cannot assume the alignment of buf, so we use the safe @@ -1480,9 +1481,9 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target, ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", __func__, packet, packet->info.rx.rx_flags); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd", - &packet->info.rx.exp_hdr, 4); + "", &packet->info.rx.exp_hdr, 4); ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header", - (u8 *)&lk_ahd, sizeof(lk_ahd)); + "", (u8 *)&lk_ahd, sizeof(lk_ahd)); status = -ENOMEM; goto fail_rx; } @@ -1518,12 +1519,12 @@ static int ath6kl_htc_rx_process_hdr(struct htc_target *target, fail_rx: if (status) ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT", - packet->buf, + "", packet->buf, packet->act_len < 256 ? packet->act_len : 256); else { if (packet->act_len > 0) ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, - "HTC - Application Msg", + "HTC - Application Msg", "", packet->buf, packet->act_len); } diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 0869ff396b57..a7117074f81c 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -340,7 +340,8 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, eid, htc_tag); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", + skb->data, skb->len); /* * HTC interface is asynchronous, if this fails, cleanup will @@ -1068,7 +1069,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) skb_put(skb, packet->act_len + HTC_HDR_LENGTH); skb_pull(skb, HTC_HDR_LENGTH); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ", + skb->data, skb->len); skb->dev = ar->net_dev; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 47fbb8e7686b..785a8c72541b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2850,7 +2850,8 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) len = skb->len; ath6kl_dbg(ATH6KL_DBG_WMI, "%s: wmi id: %d\n", __func__, id); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", datap, len); + ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", "wmi rx ", + datap, len); switch (id) { case WMI_GET_BITRATE_CMDID: From f7325b85efe1395b52ef1006dafe3c0d4ff79f15 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 14:30:58 +0300 Subject: [PATCH 100/155] ath6kl: add sdio debug messages Add extensive debug messages to sdio.c. Makes it easier to debug various problems. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 2 ++ drivers/net/wireless/ath/ath6kl/sdio.c | 36 ++++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 9f906c2d3f65..7c34826dd1e0 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -36,6 +36,8 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */ ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx and wmi frames */ ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ + ATH6KL_DBG_SDIO = BIT(16), + ATH6KL_DBG_SDIO_DUMP = BIT(17), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 4724ddfab4f7..f1dc311ee0c7 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -135,10 +135,12 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, int ret = 0; if (request & HIF_WRITE) { + /* FIXME: looks like ugly workaround for something */ if (addr >= HIF_MBOX_BASE_ADDR && addr <= HIF_MBOX_END_ADDR) addr += (HIF_MBOX_WIDTH - len); + /* FIXME: this also looks like ugly workaround */ if (addr == HIF_MBOX0_EXT_BASE_ADDR) addr += HIF_MBOX0_EXT_WIDTH - len; @@ -153,6 +155,11 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr, ret = sdio_memcpy_fromio(func, buf, addr, len); } + ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n", + request & HIF_WRITE ? "wr" : "rd", addr, + request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len); + ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len); + return ret; } @@ -173,7 +180,8 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio) list_del(&bus_req->list); spin_unlock_irqrestore(&ar_sdio->lock, flag); - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); + ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", + __func__, bus_req); return bus_req; } @@ -183,7 +191,8 @@ static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio, { unsigned long flag; - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); + ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n", + __func__, bus_req); spin_lock_irqsave(&ar_sdio->lock, flag); list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq); @@ -438,6 +447,8 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func) int status; struct ath6kl_sdio *ar_sdio; + ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n"); + ar_sdio = sdio_get_drvdata(func); atomic_set(&ar_sdio->irq_handling, 1); @@ -675,7 +686,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) MAX_SCATTER_REQUESTS, virt_scat); if (!ret) { - ath6kl_dbg(ATH6KL_DBG_ANY, + ath6kl_dbg(ATH6KL_DBG_SCATTER, "hif-scatter enabled: max scatter req : %d entries: %d\n", MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ); @@ -700,7 +711,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) return ret; } - ath6kl_dbg(ATH6KL_DBG_ANY, + ath6kl_dbg(ATH6KL_DBG_SCATTER, "Vitual scatter enabled, max_scat_req:%d, entries:%d\n", ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ); @@ -723,6 +734,9 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar) if (!(flags & MMC_PM_KEEP_POWER)) /* as host doesn't support keep power we need to bail out */ + ath6kl_dbg(ATH6KL_DBG_SDIO, + "func %d doesn't support MMC_PM_KEEP_POWER\n", + func->num); return -EINVAL; ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); @@ -758,10 +772,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func, struct ath6kl *ar; int count; - ath6kl_dbg(ATH6KL_DBG_TRC, - "%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n", - __func__, func->num, func->vendor, - func->device, func->max_blksize, func->cur_blksize); + ath6kl_dbg(ATH6KL_DBG_SDIO, + "new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", + func->num, func->vendor, func->device, + func->max_blksize, func->cur_blksize); ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL); if (!ar_sdio) @@ -820,7 +834,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, goto err_cfg80211; } - ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n"); + ath6kl_dbg(ATH6KL_DBG_SDIO, "4-bit async irq mode enabled\n"); } /* give us some time to enable, in ms */ @@ -868,6 +882,10 @@ static void ath6kl_sdio_remove(struct sdio_func *func) { struct ath6kl_sdio *ar_sdio; + ath6kl_dbg(ATH6KL_DBG_SDIO, + "removed func %d vendor 0x%x device 0x%x\n", + func->num, func->vendor, func->device); + ar_sdio = sdio_get_drvdata(func); ath6kl_stop_txrx(ar_sdio->ar); From 6bc364315aac6ab256ce3cdc00aa90cb57279a1f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 14:31:11 +0300 Subject: [PATCH 101/155] ath6kl: add debug logs for booting Just to make it easier to find out why boot fails. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 1 + drivers/net/wireless/ath/ath6kl/init.c | 71 +++++++++++++++++++++---- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 7c34826dd1e0..0fbb0ac2de23 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -38,6 +38,7 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ ATH6KL_DBG_SDIO = BIT(16), ATH6KL_DBG_SDIO_DUMP = BIT(17), + ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */ ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index e9785feeea17..876b6b28dd22 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -958,6 +958,9 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) switch (ie_id) { case ATH6KL_FW_IE_OTP_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%d B)\n", + ie_len); + ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_otp == NULL) { @@ -968,6 +971,9 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->fw_otp_len = ie_len; break; case ATH6KL_FW_IE_FW_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%d B)\n", + ie_len); + ar->fw = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw == NULL) { @@ -978,6 +984,9 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->fw_len = ie_len; break; case ATH6KL_FW_IE_PATCH_IMAGE: + ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%d B)\n", + ie_len); + ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_patch == NULL) { @@ -990,8 +999,16 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) case ATH6KL_FW_IE_RESERVED_RAM_SIZE: val = (__le32 *) data; ar->hw.reserved_ram_size = le32_to_cpup(val); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found reserved ram size ie 0x%d\n", + ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found firmware capabilities ie (%d B)\n", + ie_len); + for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { index = ALIGN(i, 8) / 8; bit = i % 8; @@ -999,6 +1016,10 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) if (data[index] & (1 << bit)) __set_bit(i, ar->fw_capabilities); } + + ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "", + ar->fw_capabilities, + sizeof(ar->fw_capabilities)); break; case ATH6KL_FW_IE_PATCH_ADDR: if (ie_len != sizeof(*val)) @@ -1006,9 +1027,13 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) val = (__le32 *) data; ar->hw.dataset_patch_addr = le32_to_cpup(val); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found patch address ie 0x%d\n", + ar->hw.dataset_patch_addr); break; default: - ath6kl_dbg(ATH6KL_DBG_TRC, "Unknown fw ie: %u\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n", le32_to_cpup(&hdr->id)); break; } @@ -1033,14 +1058,17 @@ static int ath6kl_fetch_firmwares(struct ath6kl *ar) return ret; ret = ath6kl_fetch_fw_api2(ar); - if (ret == 0) - /* fw api 2 found, use it */ + if (ret == 0) { + ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n"); return 0; + } ret = ath6kl_fetch_fw_api1(ar); if (ret) return ret; + ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n"); + return 0; } @@ -1071,18 +1099,12 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) (u8 *) &board_address, 4); } - ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n", - board_address); - /* determine where in target ram to write extended board data */ ath6kl_bmi_read(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_ext_data)), (u8 *) &board_ext_address, 4); - ath6kl_dbg(ATH6KL_DBG_TRC, "board file download addr: 0x%x\n", - board_ext_address); - if (board_ext_address == 0) { ath6kl_err("Failed to get board file target address.\n"); return -EINVAL; @@ -1107,6 +1129,10 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) board_ext_data_size)) { /* write extended board data */ + ath6kl_dbg(ATH6KL_DBG_BOOT, + "writing extended board data to 0x%x (%d B)\n", + board_ext_address, board_ext_data_size); + ret = ath6kl_bmi_write(ar, board_ext_address, ar->fw_board + board_data_size, board_ext_data_size); @@ -1131,6 +1157,9 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) return ret; } + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n", + board_address, board_data_size); + ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, board_data_size); @@ -1159,6 +1188,9 @@ static int ath6kl_upload_otp(struct ath6kl *ar) address = ar->hw.app_load_addr; + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%d B)\n", address, + ar->fw_otp_len); + ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, ar->fw_otp_len); if (ret) { @@ -1179,7 +1211,11 @@ static int ath6kl_upload_otp(struct ath6kl *ar) ar->hw.app_start_override_addr = address; + ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n", + ar->hw.app_start_override_addr); + /* execute the OTP code */ + ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address); param = 0; ath6kl_bmi_execute(ar, address, ¶m); @@ -1196,6 +1232,9 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) address = ar->hw.app_load_addr; + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%d B)\n", + address, ar->fw_len); + ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); if (ret) { @@ -1224,6 +1263,9 @@ static int ath6kl_upload_patch(struct ath6kl *ar) address = ar->hw.dataset_patch_addr; + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%d B)\n", + address, ar->fw_patch_len); + ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); if (ret) { ath6kl_err("Failed to write patch file: %d\n", ret); @@ -1396,6 +1438,15 @@ static int ath6kl_init_hw_params(struct ath6kl *ar) return -EINVAL; } + ath6kl_dbg(ATH6KL_DBG_BOOT, + "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n", + ar->version.target_ver, ar->target_type, + ar->hw.dataset_patch_addr, ar->hw.app_load_addr); + ath6kl_dbg(ATH6KL_DBG_BOOT, + "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x", + ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr, + ar->hw.reserved_ram_size); + return 0; } @@ -1472,6 +1523,8 @@ static int ath6kl_init(struct net_device *dev) &ar->flag), WMI_TIMEOUT); + ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); + if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ATH6KL_ABI_VERSION, ar->version.abi_ver); From b9b6ee603923be45c4022a0dce5fa8ccf4284524 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 27 Sep 2011 14:31:21 +0300 Subject: [PATCH 102/155] ath6kl: improve wmi debug messages Add a new debug level ATH6KL_DBG_WMI_DUMP and other minor improvements to the wmi debug messages. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 3 +- drivers/net/wireless/ath/ath6kl/wmi.c | 44 ++++++++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 0fbb0ac2de23..9288a3ce1e39 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -34,11 +34,12 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_TRC = BIT(11), /* generic func tracing */ ATH6KL_DBG_SCATTER = BIT(12), /* hif scatter tracing */ ATH6KL_DBG_WLAN_CFG = BIT(13), /* cfg80211 i/f file tracing */ - ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx and wmi frames */ + ATH6KL_DBG_RAW_BYTES = BIT(14), /* dump tx/rx frames */ ATH6KL_DBG_AGGR = BIT(15), /* aggregation */ ATH6KL_DBG_SDIO = BIT(16), ATH6KL_DBG_SDIO_DUMP = BIT(17), ATH6KL_DBG_BOOT = BIT(18), /* driver init and fw boot */ + ATH6KL_DBG_WMI_DUMP = BIT(19), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 785a8c72541b..a7de23cbd2c7 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -721,8 +721,12 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len) /* STA/IBSS mode connection event */ - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM\n", - __func__, le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid); + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n", + le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid, + le16_to_cpu(ev->u.sta.listen_intvl), + le16_to_cpu(ev->u.sta.beacon_intvl), + le32_to_cpu(ev->u.sta.nw_type)); /* Start of assoc rsp IEs */ pie = ev->assoc_info + ev->beacon_ie_len + @@ -822,7 +826,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); - ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl: Regpair used: 0x%0x\n", + ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", regpair->regDmnEnum); } @@ -832,7 +836,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2); - ath6kl_dbg(ATH6KL_DBG_WMI, "ath6kl: Country alpha2 being used: %c%c\n", + ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n", alpha2[0], alpha2[1]); } } @@ -847,6 +851,11 @@ static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len) ev = (struct wmi_disconnect_event *) datap; + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n", + le16_to_cpu(ev->proto_reason_status), ev->bssid, + ev->disconn_reason, ev->assoc_resp_len); + wmi->is_wmm_enabled = false; wmi->pair_crypto_type = NONE_CRYPT; wmi->grp_crypto_type = NONE_CRYPT; @@ -1526,11 +1535,14 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb, enum htc_endpoint_id ep_id = wmi->ep_id; int ret; - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: cmd_id=%d\n", __func__, cmd_id); - if (WARN_ON(skb == NULL)) return -EINVAL; + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", + cmd_id, skb->len, sync_flag); + ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ", + skb->data, skb->len); + if (sync_flag >= END_WMIFLAG) { dev_kfree_skb(skb); return -EINVAL; @@ -1589,6 +1601,13 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type, struct wmi_connect_cmd *cc; int ret; + ath6kl_dbg(ATH6KL_DBG_WMI, + "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d " + "type %d dot11_auth %d auth %d pairwise %d group %d\n", + bssid, channel, ctrl_flags, ssid_len, nw_type, + dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto); + ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len); + wmi->traffic_class = 100; if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT)) @@ -1634,6 +1653,9 @@ int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel) struct wmi_reconnect_cmd *cc; int ret; + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n", + bssid, channel); + wmi->traffic_class = 100; skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd)); @@ -1656,6 +1678,8 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi) { int ret; + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n"); + wmi->traffic_class = 100; /* Disconnect command does not need to do a SYNC before. */ @@ -2808,12 +2832,14 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) switch (id) { case WMIX_HB_CHALLENGE_RESP_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); break; case WMIX_DBGLOG_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len); break; default: - ath6kl_err("unknown cmd id 0x%x\n", id); + ath6kl_warn("unknown cmd id 0x%x\n", id); wmi->stat.cmd_id_err++; ret = -EINVAL; break; @@ -2849,8 +2875,8 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb) datap = skb->data; len = skb->len; - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: wmi id: %d\n", __func__, id); - ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", "wmi rx ", + ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len); + ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ", datap, len); switch (id) { From c6efe578fc5dd02463d2ee20343494da56bdd3a9 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 28 Sep 2011 18:32:34 +1000 Subject: [PATCH 103/155] wireless/ath6kl: use of module_param requires the inclusion of moduleparam.h Otheriwse the module.h split up fails like this: drivers/net/wireless/ath/ath6kl/init.c:27:26: error: expected ')' before 'uint' Signed-off-by: Stephen Rothwell Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 876b6b28dd22..5995bb9ead8d 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -15,6 +15,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include "core.h" From b6f98044a6cbeba8234a3d433d715e9ef36880c4 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Fri, 23 Sep 2011 10:01:30 +0200 Subject: [PATCH 104/155] Bluetooth: Fix possible NULL pointer dereference Checking conn->pending_sec_level if there is no connection leads to potential null pointer dereference. Don't process pin_code_request_event at all if no connection exists. Signed-off-by: Waldemar Rymarkiewicz Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 35083f2aa2ea..7390ba9d4f6e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2174,7 +2174,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn && conn->state == BT_CONNECTED) { + if (!conn) + goto unlock; + + if (conn->state == BT_CONNECTED) { hci_conn_hold(conn); conn->disc_timeout = HCI_PAIRING_TIMEOUT; hci_conn_put(conn); @@ -2194,6 +2197,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); } +unlock: hci_dev_unlock(hdev); } From e95beb414168f8fcae195b5a77be29b3362d6904 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Mon, 26 Sep 2011 20:48:35 -0300 Subject: [PATCH 105/155] Bluetooth: hci_le_adv_report_evt code refactoring There is no reason to treat the first advertising entry differently from the potential other ones. Besides, the current implementation can easily leads to typos. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7390ba9d4f6e..d7d96b6b1f0d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2838,19 +2838,17 @@ unlock: static inline void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_le_advertising_info *ev; - u8 num_reports; - - num_reports = skb->data[0]; - ev = (void *) &skb->data[1]; + u8 num_reports = skb->data[0]; + void *ptr = &skb->data[1]; hci_dev_lock(hdev); - hci_add_adv_entry(hdev, ev); + while (num_reports--) { + struct hci_ev_le_advertising_info *ev = ptr; - while (--num_reports) { - ev = (void *) (ev->data + ev->length + 1); hci_add_adv_entry(hdev, ev); + + ptr += sizeof(*ev) + ev->length + 1; } hci_dev_unlock(hdev); From c510eae377c773241ff0b6369a8f3581da941a51 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 21 Sep 2011 11:41:45 +0200 Subject: [PATCH 106/155] btusb: add device entry for Broadcom SoftSailing From 0cea73465cd22373c5cd43a3edd25fbd4bb532ef Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 21 Sep 2011 11:37:15 +0200 Subject: [PATCH] btusb: add device entry for Broadcom SoftSailing This device declares itself to be vendor specific It therefore needs to be added to the device table to make btusb bind. Signed-off-by: Oliver Neukum Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 2755c1a9c38e..675246a6f7ef 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -60,6 +60,9 @@ static struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + /* Broadcom SoftSailing reporting vendor specific */ + { USB_DEVICE(0x05ac, 0x21e1) }, + /* Apple MacBookPro 7,1 */ { USB_DEVICE(0x05ac, 0x8213) }, From 4ca8c452a655d96639975d132f586f2829f6564e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 28 Sep 2011 12:30:59 +0300 Subject: [PATCH 107/155] wireless: at76c50x: use native hex_pack_byte() method Signed-off-by: Andy Shevchenko Cc: "John W. Linville" Tested-by: Pavel Roskin Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 298601436ee2..39322d4121b7 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -500,10 +500,9 @@ exit: #define HEX2STR_BUFFERS 4 #define HEX2STR_MAX_LEN 64 -#define BIN2HEX(x) ((x) < 10 ? '0' + (x) : (x) + 'A' - 10) /* Convert binary data into hex string */ -static char *hex2str(void *buf, int len) +static char *hex2str(void *buf, size_t len) { static atomic_t a = ATOMIC_INIT(0); static char bufs[HEX2STR_BUFFERS][3 * HEX2STR_MAX_LEN + 1]; @@ -514,18 +513,17 @@ static char *hex2str(void *buf, int len) if (len > HEX2STR_MAX_LEN) len = HEX2STR_MAX_LEN; - if (len <= 0) { - ret[0] = '\0'; - return ret; - } + if (len == 0) + goto exit; while (len--) { - *obuf++ = BIN2HEX(*ibuf >> 4); - *obuf++ = BIN2HEX(*ibuf & 0xf); + obuf = pack_hex_byte(obuf, *ibuf++); *obuf++ = '-'; - ibuf++; } - *(--obuf) = '\0'; + obuf--; + +exit: + *obuf = '\0'; return ret; } From 89888e368eebb8d5c3dbf58425b95fc773aee511 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 24 Sep 2011 18:48:26 +0200 Subject: [PATCH 108/155] mac80211: max_tp_rate2 management of minstrel_ht I noticed a possible issue in the max_tp_rate2 management of minstrel_ht. In particular, if we look up just among max_tp_rate2 of each group it will be possible that the selected rate will not be the mcs with second maximum throughput. I wrote this simple patch. Signed-off-by: Lorenzo Bianconi Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel_ht.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index e19249b0f971..cdb28535716b 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -281,6 +281,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mr = minstrel_get_ratestats(mi, mg->max_tp_rate); if (cur_tp < mr->cur_tp) { + mi->max_tp_rate2 = mi->max_tp_rate; + cur_tp2 = cur_tp; mi->max_tp_rate = mg->max_tp_rate; cur_tp = mr->cur_tp; } From 185d1589ccc9f49afcdaede480523df2bfec7c01 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 26 Sep 2011 21:48:39 +0530 Subject: [PATCH 109/155] ath9k: Remove unnecessary AMPDU check at tx status Fill the ampdu_[ack]_len for both aggregation and normal frames. So that we could avoid unnecesary conditional at tx status. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 6 ------ drivers/net/wireless/ath/ath9k/xmit.c | 5 ++--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 4f1301881137..8448281dd069 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1362,12 +1362,6 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) return; - if (!(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) { - tx_info->status.ampdu_ack_len = - (tx_info->flags & IEEE80211_TX_STAT_ACK ? 1 : 0); - tx_info->status.ampdu_len = 1; - } - if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) tx_status = 1; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index fa3dcfdf7174..f5d4764888b9 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2043,10 +2043,9 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, tx_info->flags |= IEEE80211_TX_STAT_AMPDU; BUG_ON(nbad > nframes); - - tx_info->status.ampdu_len = nframes; - tx_info->status.ampdu_ack_len = nframes - nbad; } + tx_info->status.ampdu_len = nframes; + tx_info->status.ampdu_ack_len = nframes - nbad; if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 && (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) == 0) { From f73c604cfbf7f611e3ec129a0548fcbe8574d180 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 26 Sep 2011 22:16:56 +0530 Subject: [PATCH 110/155] ath9k: Remove redundant my beacon check at ath_rx_ps_beacon Make use of the rx status's is_mybeacon in order to avoid redundant memcmp. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 49843500fe7c..f658ec60b510 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -586,22 +586,11 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) { - struct ieee80211_mgmt *mgmt; struct ath_common *common = ath9k_hw_common(sc->sc_ah); if (skb->len < 24 + 8 + 2 + 2) return; - mgmt = (struct ieee80211_mgmt *)skb->data; - if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) { - /* TODO: This doesn't work well if you have stations - * associated to two different APs because curbssid - * is just the last AP that any of the stations associated - * with. - */ - return; /* not from our current AP */ - } - sc->ps_flags &= ~PS_WAIT_FOR_BEACON; if (sc->ps_flags & PS_BEACON_SYNC) { @@ -637,7 +626,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) } } -static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) +static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) { struct ieee80211_hdr *hdr; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -646,7 +635,7 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) /* Process Beacon and CAB receive in PS state */ if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc)) - && ieee80211_is_beacon(hdr->frame_control)) + && mybeacon) ath_rx_ps_beacon(sc, skb); else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && (ieee80211_is_data(hdr->frame_control) || @@ -1952,10 +1941,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((sc->ps_flags & (PS_WAIT_FOR_BEACON | - PS_WAIT_FOR_CAB | - PS_WAIT_FOR_PSPOLL_DATA)) || - ath9k_check_auto_sleep(sc)) - ath_rx_ps(sc, skb); + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA)) || + ath9k_check_auto_sleep(sc)) + ath_rx_ps(sc, skb, rs.is_mybeacon); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3) From 82df4d38a0194ad1804d1ab4716577d194be5a53 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 26 Sep 2011 20:37:23 -0700 Subject: [PATCH 111/155] mwifiex: remove unnecessary mwifiex_dump_station_info() call An extra call to mwifiex_dump_station_info() routine in get_station callback function is redundant Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 0ddcdca63cf7..a7c773ebfe2f 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -565,8 +565,6 @@ mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - mwifiex_dump_station_info(priv, sinfo); - if (!priv->media_connected) return -ENOENT; if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) From cbaaf592b742ccecfd066e796cdb1eda461f4db2 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 26 Sep 2011 20:37:24 -0700 Subject: [PATCH 112/155] mwifiex: remove unreachable code In disconnected state "iw dev mlan0 link" command will return from cfg80211 stack itself. We also have an error check in mwifiex_cfg80211_get_station() routine. Therefore the code under "if (!priv->media_connected)" condition in mwifiex_rate_ioctl_get_rate_value() routine becomes unreachable. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sta_ioctl.c | 46 ++---------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index a9dfeb1b4ace..520800b618e7 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -720,51 +720,9 @@ done: static int mwifiex_rate_ioctl_get_rate_value(struct mwifiex_private *priv, struct mwifiex_rate_cfg *rate_cfg) { - struct mwifiex_adapter *adapter = priv->adapter; - rate_cfg->is_rate_auto = priv->is_data_rate_auto; - if (!priv->media_connected) { - switch (adapter->config_bands) { - case BAND_B: - /* Return the lowest supported rate for B band */ - rate_cfg->rate = supported_rates_b[0] & 0x7f; - break; - case BAND_G: - case BAND_G | BAND_GN: - /* Return the lowest supported rate for G band */ - rate_cfg->rate = supported_rates_g[0] & 0x7f; - break; - case BAND_B | BAND_G: - case BAND_A | BAND_B | BAND_G: - case BAND_A | BAND_B: - case BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN: - case BAND_B | BAND_G | BAND_GN: - /* Return the lowest supported rate for BG band */ - rate_cfg->rate = supported_rates_bg[0] & 0x7f; - break; - case BAND_A: - case BAND_A | BAND_G: - case BAND_A | BAND_G | BAND_AN | BAND_GN: - case BAND_A | BAND_AN: - /* Return the lowest supported rate for A band */ - rate_cfg->rate = supported_rates_a[0] & 0x7f; - break; - case BAND_GN: - /* Return the lowest supported rate for N band */ - rate_cfg->rate = supported_rates_n[0] & 0x7f; - break; - default: - dev_warn(adapter->dev, "invalid band %#x\n", - adapter->config_bands); - break; - } - } else { - return mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL); - } - - return 0; + return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL); } /* From 4ec6f9c0c9602bf8e6972eca470fbd1e99c5b222 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 26 Sep 2011 20:37:25 -0700 Subject: [PATCH 113/155] mwifiex: fix Tx data rate display issue "iw dev mlan0 link" shows wrong data rate, because data rate is not sent properly to cfg80211 stack. Also stack is not updated with mcs and Tx data flags information. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a7c773ebfe2f..e61a6c0dd0ff 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -543,12 +543,28 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, ret = -EFAULT; } + /* + * Bit 0 in tx_htinfo indicates that current Tx rate is 11n rate. Valid + * MCS index values for us are 0 to 7. + */ + if ((priv->tx_htinfo & BIT(0)) && (priv->tx_rate < 8)) { + sinfo->txrate.mcs = priv->tx_rate; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + /* 40MHz rate */ + if (priv->tx_htinfo & BIT(1)) + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + /* SGI enabled */ + if (priv->tx_htinfo & BIT(2)) + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } + sinfo->rx_bytes = priv->stats.rx_bytes; sinfo->tx_bytes = priv->stats.tx_bytes; sinfo->rx_packets = priv->stats.rx_packets; sinfo->tx_packets = priv->stats.tx_packets; sinfo->signal = priv->qual_level; - sinfo->txrate.legacy = rate.rate; + /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = rate.rate * 5; return ret; } From 93a1df48d224296fb527d32fbec4d5162828feb4 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Mon, 26 Sep 2011 20:37:26 -0700 Subject: [PATCH 114/155] mwifiex: add cfg80211 handlers add/del_virtual_intf Making adding and deleting virtual interfaces dynamic. Adding handlers for creating and deleting virtual interface with given name and dev respectively. Also, creating default interface of type station on insmod of the driver. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 156 ++++++++++++-- drivers/net/wireless/mwifiex/cfg80211.h | 3 +- drivers/net/wireless/mwifiex/decl.h | 8 - drivers/net/wireless/mwifiex/init.c | 2 +- drivers/net/wireless/mwifiex/main.c | 263 +++++------------------- drivers/net/wireless/mwifiex/main.h | 18 +- 6 files changed, 207 insertions(+), 243 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e61a6c0dd0ff..462c71067bfb 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1162,8 +1162,150 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } +/* + * create a new virtual interface with the given name + */ +struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + struct mwifiex_adapter *adapter; + struct net_device *dev; + void *mdev_priv; + + if (!priv) + return NULL; + + adapter = priv->adapter; + if (!adapter) + return NULL; + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + if (priv->bss_mode) { + wiphy_err(wiphy, "cannot create multiple" + " station/adhoc interfaces\n"); + return NULL; + } + + if (type == NL80211_IFTYPE_UNSPECIFIED) + priv->bss_mode = NL80211_IFTYPE_STATION; + else + priv->bss_mode = type; + + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_index = 0; + priv->bss_num = 0; + + break; + default: + wiphy_err(wiphy, "type not supported\n"); + return NULL; + } + + dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), name, + ether_setup, 1); + if (!dev) { + wiphy_err(wiphy, "no memory available for netdevice\n"); + goto error; + } + + dev_net_set(dev, wiphy_net(wiphy)); + dev->ieee80211_ptr = priv->wdev; + dev->ieee80211_ptr->iftype = priv->bss_mode; + memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); + + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; + dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; + + mdev_priv = netdev_priv(dev); + *((unsigned long *) mdev_priv) = (unsigned long) priv; + + priv->netdev = dev; + mwifiex_init_priv_params(priv, dev); + + SET_NETDEV_DEV(dev, adapter->dev); + + /* Register network device */ + if (register_netdevice(dev)) { + wiphy_err(wiphy, "cannot register virtual network device\n"); + goto error; + } + + sema_init(&priv->async_sem, 1); + priv->scan_pending_on_block = false; + + dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_init(priv); +#endif + return dev; +error: + if (dev && (dev->reg_state == NETREG_UNREGISTERED)) + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + + return NULL; +} +EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); + +/* + * del_virtual_intf: remove the virtual interface determined by dev + */ +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); + + if (!priv || !dev) + return 0; + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_remove(priv); +#endif + + if (!netif_queue_stopped(priv->netdev)) + netif_stop_queue(priv->netdev); + + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdevice(dev); + + if (dev->reg_state == NETREG_UNREGISTERED) + free_netdev(dev); + + /* Clear the priv in adapter */ + priv->netdev = NULL; + + priv->media_connected = false; + + cancel_work_sync(&priv->cfg_workqueue); + flush_workqueue(priv->workqueue); + destroy_workqueue(priv->workqueue); + + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); + /* station cfg80211 operations */ static struct cfg80211_ops mwifiex_cfg80211_ops = { + .add_virtual_intf = mwifiex_add_virtual_intf, + .del_virtual_intf = mwifiex_del_virtual_intf, .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, .scan = mwifiex_cfg80211_scan, .connect = mwifiex_cfg80211_connect, @@ -1188,8 +1330,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { * default parameters and handler function pointers, and finally * registers the device. */ -int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, - struct mwifiex_private *priv) +int mwifiex_register_cfg80211(struct mwifiex_private *priv) { int ret; void *wdev_priv; @@ -1229,7 +1370,7 @@ int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, wdev->wiphy->cipher_suites = mwifiex_cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); - memcpy(wdev->wiphy->perm_addr, mac, 6); + memcpy(wdev->wiphy->perm_addr, priv->curr_addr, ETH_ALEN); wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; /* We are using custom domains */ @@ -1259,17 +1400,8 @@ int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, "info: successfully registered wiphy device\n"); } - dev_net_set(dev, wiphy_net(wdev->wiphy)); - dev->ieee80211_ptr = wdev; - memcpy(dev->dev_addr, wdev->wiphy->perm_addr, 6); - memcpy(dev->perm_addr, wdev->wiphy->perm_addr, 6); - SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); priv->wdev = wdev; - dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; - dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; - return ret; } diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h index c4db8f36aa16..8d010f2500c5 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.h +++ b/drivers/net/wireless/mwifiex/cfg80211.h @@ -24,8 +24,7 @@ #include "main.h" -int mwifiex_register_cfg80211(struct net_device *, u8 *, - struct mwifiex_private *); +int mwifiex_register_cfg80211(struct mwifiex_private *); void mwifiex_cfg80211_results(struct work_struct *work); #endif diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 94ddc9038cb3..6ca62c809cb9 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -114,14 +114,6 @@ struct mwifiex_txinfo { u8 bss_index; }; -struct mwifiex_bss_attr { - u8 bss_type; - u8 frame_type; - u8 active; - u8 bss_priority; - u8 bss_num; -}; - enum mwifiex_wmm_ac_e { WMM_AC_BK, WMM_AC_BE, diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 26e685a31bc0..e1076b46401e 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -76,7 +76,7 @@ static int mwifiex_init_priv(struct mwifiex_private *priv) memset(priv->curr_addr, 0xff, ETH_ALEN); priv->pkt_tx_ctrl = 0; - priv->bss_mode = NL80211_IFTYPE_STATION; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; priv->data_rate = 0; /* Initially indicate the rate as auto */ priv->is_data_rate_auto = true; priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 53579ad83e5c..8b05b4f5ffe2 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -26,21 +26,6 @@ const char driver_version[] = "mwifiex " VERSION " (%s) "; -static struct mwifiex_bss_attr mwifiex_bss_sta[] = { - {MWIFIEX_BSS_TYPE_STA, MWIFIEX_DATA_FRAME_TYPE_ETH_II, true, 0, 0}, -}; - -static int drv_mode = DRV_MODE_STA; - -/* Supported drv_mode table */ -static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = { - { - .drv_mode = DRV_MODE_STA, - .intf_num = ARRAY_SIZE(mwifiex_bss_sta), - .bss_attr = mwifiex_bss_sta, - }, -}; - /* * This function registers the device and performs all the necessary * initializations. @@ -57,7 +42,6 @@ static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = { * proper cleanup before exiting. */ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, - struct mwifiex_drv_mode *drv_mode_ptr, void **padapter) { struct mwifiex_adapter *adapter; @@ -78,42 +62,19 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, goto error; adapter->priv_num = 0; - for (i = 0; i < drv_mode_ptr->intf_num; i++) { - adapter->priv[i] = NULL; - if (!drv_mode_ptr->bss_attr[i].active) - continue; - - /* Allocate memory for private structure */ - adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private), - GFP_KERNEL); - if (!adapter->priv[i]) { - dev_err(adapter->dev, "%s: failed to alloc priv[%d]\n", - __func__, i); - goto error; - } - - adapter->priv_num++; - adapter->priv[i]->adapter = adapter; - /* Save bss_type, frame_type & bss_priority */ - adapter->priv[i]->bss_type = drv_mode_ptr->bss_attr[i].bss_type; - adapter->priv[i]->frame_type = - drv_mode_ptr->bss_attr[i].frame_type; - adapter->priv[i]->bss_priority = - drv_mode_ptr->bss_attr[i].bss_priority; - - if (drv_mode_ptr->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_STA) - adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_STA; - else if (drv_mode_ptr->bss_attr[i].bss_type == - MWIFIEX_BSS_TYPE_UAP) - adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_UAP; - - /* Save bss_index & bss_num */ - adapter->priv[i]->bss_index = i; - adapter->priv[i]->bss_num = drv_mode_ptr->bss_attr[i].bss_num; + /* Allocate memory for private structure */ + adapter->priv[0] = kzalloc(sizeof(struct mwifiex_private), + GFP_KERNEL); + if (!adapter->priv[0]) { + dev_err(adapter->dev, "%s: failed to alloc priv[0]\n", + __func__); + goto error; } - adapter->drv_mode = drv_mode_ptr; + adapter->priv_num++; + + adapter->priv[0]->adapter = adapter; if (mwifiex_init_lock_list(adapter)) goto error; @@ -127,8 +88,10 @@ error: dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n"); mwifiex_free_lock_list(adapter); - for (i = 0; i < drv_mode_ptr->intf_num; i++) + + for (i = 0; i < adapter->priv_num; i++) kfree(adapter->priv[i]); + kfree(adapter); return -1; @@ -315,38 +278,6 @@ exit_main_proc: return ret; } -/* - * This function initializes the software. - * - * The main work includes allocating and initializing the adapter structure - * and initializing the private structures. - */ -static int -mwifiex_init_sw(void *card, struct mwifiex_if_ops *if_ops, void **padapter) -{ - int i; - struct mwifiex_drv_mode *drv_mode_ptr; - - /* find mwifiex_drv_mode entry from mwifiex_drv_mode_tbl */ - drv_mode_ptr = NULL; - for (i = 0; i < ARRAY_SIZE(mwifiex_drv_mode_tbl); i++) { - if (mwifiex_drv_mode_tbl[i].drv_mode == drv_mode) { - drv_mode_ptr = &mwifiex_drv_mode_tbl[i]; - break; - } - } - - if (!drv_mode_ptr) { - pr_err("invalid drv_mode=%d\n", drv_mode); - return -1; - } - - if (mwifiex_register(card, if_ops, drv_mode_ptr, padapter)) - return -1; - - return 0; -} - /* * This function frees the adapter structure. * @@ -649,8 +580,8 @@ static const struct net_device_ops mwifiex_netdev_ops = { * * In addition, the CFG80211 work queue is also created. */ -static void -mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev) { dev->netdev_ops = &mwifiex_netdev_ops; /* Initialize private structure */ @@ -663,118 +594,6 @@ mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); } -/* - * This function adds a new logical interface. - * - * It allocates, initializes and registers the interface by performing - * the following opearations - - * - Allocate a new net device structure - * - Assign device name - * - Register the new device with CFG80211 subsystem - * - Initialize semaphore and private structure - * - Register the new device with kernel - * - Create the complete debug FS structure if configured - */ -static struct mwifiex_private *mwifiex_add_interface( - struct mwifiex_adapter *adapter, - u8 bss_index, u8 bss_type) -{ - struct net_device *dev; - struct mwifiex_private *priv; - void *mdev_priv; - - dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), "mlan%d", - ether_setup, 1); - if (!dev) { - dev_err(adapter->dev, "no memory available for netdevice\n"); - goto error; - } - - if (mwifiex_register_cfg80211(dev, adapter->priv[bss_index]->curr_addr, - adapter->priv[bss_index]) != 0) { - dev_err(adapter->dev, "cannot register netdevice with cfg80211\n"); - goto error; - } - /* Save the priv pointer in netdev */ - priv = adapter->priv[bss_index]; - mdev_priv = netdev_priv(dev); - *((unsigned long *) mdev_priv) = (unsigned long) priv; - - priv->netdev = dev; - - sema_init(&priv->async_sem, 1); - priv->scan_pending_on_block = false; - - mwifiex_init_priv_params(priv, dev); - - SET_NETDEV_DEV(dev, adapter->dev); - - /* Register network device */ - if (register_netdev(dev)) { - dev_err(adapter->dev, "cannot register virtual network device\n"); - goto error; - } - - dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_init(priv); -#endif - return priv; -error: - if (dev) - free_netdev(dev); - return NULL; -} - -/* - * This function removes a logical interface. - * - * It deregisters, resets and frees the interface by performing - * the following operations - - * - Disconnect the device if connected, send wireless event to - * notify applications. - * - Remove the debug FS structure if configured - * - Unregister the device from kernel - * - Free the net device structure - * - Cancel all works and destroy work queue - * - Unregister and free the wireless device from CFG80211 subsystem - */ -static void -mwifiex_remove_interface(struct mwifiex_adapter *adapter, u8 bss_index) -{ - struct net_device *dev; - struct mwifiex_private *priv = adapter->priv[bss_index]; - - if (!priv) - return; - dev = priv->netdev; - - if (priv->media_connected) - priv->media_connected = false; - -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_remove(priv); -#endif - /* Last reference is our one */ - dev_dbg(adapter->dev, "info: %s: refcnt = %d\n", - dev->name, netdev_refcnt_read(dev)); - - if (dev->reg_state == NETREG_REGISTERED) - unregister_netdev(dev); - - /* Clear the priv in adapter */ - priv->netdev = NULL; - if (dev) - free_netdev(dev); - - cancel_work_sync(&priv->cfg_workqueue); - flush_workqueue(priv->workqueue); - destroy_workqueue(priv->workqueue); - wiphy_unregister(priv->wdev->wiphy); - wiphy_free(priv->wdev->wiphy); - kfree(priv->wdev); -} - /* * This function check if command is pending. */ @@ -847,14 +666,14 @@ int mwifiex_add_card(void *card, struct semaphore *sem, struct mwifiex_if_ops *if_ops) { - int i; struct mwifiex_adapter *adapter; char fmt[64]; + struct mwifiex_private *priv; if (down_interruptible(sem)) goto exit_sem_err; - if (mwifiex_init_sw(card, if_ops, (void **)&adapter)) { + if (mwifiex_register(card, if_ops, (void **)&adapter)) { pr_err("%s: software init failed\n", __func__); goto err_init_sw; } @@ -888,14 +707,26 @@ mwifiex_add_card(void *card, struct semaphore *sem, goto err_init_fw; } - /* Add interfaces */ - for (i = 0; i < adapter->drv_mode->intf_num; i++) { - if (!mwifiex_add_interface(adapter, i, - adapter->drv_mode->bss_attr[i].bss_type)) { - goto err_add_intf; - } + priv = adapter->priv[0]; + + if (mwifiex_register_cfg80211(priv) != 0) { + dev_err(adapter->dev, "cannot register netdevice" + " with cfg80211\n"); + goto err_init_fw; } + rtnl_lock(); + /* Create station interface by default */ + if (!mwifiex_add_virtual_intf(priv->wdev->wiphy, "mlan%d", + NL80211_IFTYPE_STATION, NULL, NULL)) { + rtnl_unlock(); + dev_err(adapter->dev, "cannot create default station" + " interface\n"); + goto err_add_intf; + } + + rtnl_unlock(); + up(sem); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); @@ -904,8 +735,9 @@ mwifiex_add_card(void *card, struct semaphore *sem, return 0; err_add_intf: - for (i = 0; i < adapter->priv_num; i++) - mwifiex_remove_interface(adapter, i); + rtnl_lock(); + mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev); + rtnl_unlock(); err_init_fw: pr_debug("info: %s: unregister device\n", __func__); adapter->if_ops.unregister_dev(adapter); @@ -960,7 +792,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) /* Stop data */ for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; - if (priv) { + if (priv && priv->netdev) { if (!netif_queue_stopped(priv->netdev)) netif_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) @@ -985,9 +817,20 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) atomic_read(&adapter->cmd_pending)); } - /* Remove interface */ - for (i = 0; i < adapter->priv_num; i++) - mwifiex_remove_interface(adapter, i); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + rtnl_lock(); + mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev); + rtnl_unlock(); + } + + wiphy_unregister(priv->wdev->wiphy); + wiphy_free(priv->wdev->wiphy); + kfree(priv->wdev); mwifiex_terminate_workqueue(adapter); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 1e801328a558..4f4042809f23 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -45,15 +45,6 @@ enum { MWIFIEX_SYNC_CMD }; -#define DRV_MODE_STA 0x1 - -struct mwifiex_drv_mode { - u16 drv_mode; - u16 intf_num; - struct mwifiex_bss_attr *bss_attr; -}; - - #define MWIFIEX_MAX_AP 64 #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) @@ -546,7 +537,6 @@ struct mwifiex_if_ops { struct mwifiex_adapter { struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; u8 priv_num; - struct mwifiex_drv_mode *drv_mode; const struct firmware *firmware; char fw_name[32]; struct device *dev; @@ -792,6 +782,8 @@ int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int is_command_pending(struct mwifiex_adapter *adapter); +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev); /* * This function checks if the queuing is RA based or not. @@ -966,6 +958,12 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, int mwifiex_check_network_compatibility(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); +struct net_device *mwifiex_add_virtual_intf(struct wiphy *wiphy, + char *name, enum nl80211_iftype type, + u32 *flags, struct vif_params *params); +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev); + + #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); From 970ba6a64db9e8a01193d36a7345745527c30463 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 27 Sep 2011 00:48:20 -0500 Subject: [PATCH 115/155] rtlwifi: Remove unused routine _usb_readN_sync Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/usb.c | 10 ---------- drivers/net/wireless/rtlwifi/wifi.h | 2 -- 2 files changed, 12 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 4bf3cf457ef0..2e544a0c7c05 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -211,15 +211,6 @@ static int _usb_nbytes_read_write(struct usb_device *udev, bool read, u32 addr, return status; } -static int _usb_readN_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len, - u8 *pdata) -{ - struct device *dev = rtlpriv->io.dev; - - return _usb_nbytes_read_write(to_usb_device(dev), true, addr, len, - pdata); -} - static int _usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, u16 len, u8 *pdata) { @@ -243,7 +234,6 @@ static void _rtl_usb_io_handler_init(struct device *dev, rtlpriv->io.read8_sync = _usb_read8_sync; rtlpriv->io.read16_sync = _usb_read16_sync; rtlpriv->io.read32_sync = _usb_read32_sync; - rtlpriv->io.readN_sync = _usb_readN_sync; } static void _rtl_usb_io_handler_release(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 615f6b4463e6..31b3be98e50c 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -950,8 +950,6 @@ struct rtl_io { u8(*read8_sync) (struct rtl_priv *rtlpriv, u32 addr); u16(*read16_sync) (struct rtl_priv *rtlpriv, u32 addr); u32(*read32_sync) (struct rtl_priv *rtlpriv, u32 addr); - int (*readN_sync) (struct rtl_priv *rtlpriv, u32 addr, u16 len, - u8 *pdata); }; From ffca287118d8c9a0a62e16a5ed96ff004caadeda Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 27 Sep 2011 00:48:21 -0500 Subject: [PATCH 116/155] rtlwifi: Remove unused _usb_nbytes_read_write and _usb_writeN_sync Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/usb.c | 30 ----------------------------- drivers/net/wireless/rtlwifi/wifi.h | 6 ++---- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 2e544a0c7c05..b42c2e2b2055 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -191,35 +191,6 @@ static void _usb_write32_async(struct rtl_priv *rtlpriv, u32 addr, u32 val) _usb_write_async(to_usb_device(dev), addr, val, 4); } -static int _usb_nbytes_read_write(struct usb_device *udev, bool read, u32 addr, - u16 len, u8 *pdata) -{ - int status; - u8 request; - u16 wvalue; - u16 index; - - request = REALTEK_USB_VENQT_CMD_REQ; - index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */ - wvalue = (u16)addr; - if (read) - status = _usbctrl_vendorreq_sync_read(udev, request, wvalue, - index, pdata, len); - else - status = _usbctrl_vendorreq_async_write(udev, request, wvalue, - index, pdata, len); - return status; -} - -static int _usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, u16 len, - u8 *pdata) -{ - struct device *dev = rtlpriv->io.dev; - - return _usb_nbytes_read_write(to_usb_device(dev), false, addr, len, - pdata); -} - static void _rtl_usb_io_handler_init(struct device *dev, struct ieee80211_hw *hw) { @@ -230,7 +201,6 @@ static void _rtl_usb_io_handler_init(struct device *dev, rtlpriv->io.write8_async = _usb_write8_async; rtlpriv->io.write16_async = _usb_write16_async; rtlpriv->io.write32_async = _usb_write32_async; - rtlpriv->io.writeN_async = _usb_writeN_async; rtlpriv->io.read8_sync = _usb_read8_sync; rtlpriv->io.read16_sync = _usb_read16_sync; rtlpriv->io.read32_sync = _usb_read32_sync; diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 31b3be98e50c..3126485393d9 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -942,10 +942,8 @@ struct rtl_io { unsigned long pci_base_addr; /*device I/O address */ void (*write8_async) (struct rtl_priv *rtlpriv, u32 addr, u8 val); - void (*write16_async) (struct rtl_priv *rtlpriv, u32 addr, u16 val); - void (*write32_async) (struct rtl_priv *rtlpriv, u32 addr, u32 val); - int (*writeN_async) (struct rtl_priv *rtlpriv, u32 addr, u16 len, - u8 *pdata); + void (*write16_async) (struct rtl_priv *rtlpriv, u32 addr, __le16 val); + void (*write32_async) (struct rtl_priv *rtlpriv, u32 addr, __le32 val); u8(*read8_sync) (struct rtl_priv *rtlpriv, u32 addr); u16(*read16_sync) (struct rtl_priv *rtlpriv, u32 addr); From 3b9ce80ce96aeaeacab5e26442987df45584a049 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 27 Sep 2011 20:56:12 +0200 Subject: [PATCH 117/155] cfg80211/mac80211: apply station uAPSD parameters selectively Currently, when hostapd sets the station as authorized we also overwrite its uAPSD parameter. This obviously leads to buggy behaviour (later, with my patches that actually add uAPSD support). To fix this, only apply those parameters if they were actually set in nl80211, and to achieve that add a bitmap of things to apply. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 12 ++++++++++++ net/mac80211/cfg.c | 6 ++++-- net/wireless/nl80211.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 31d823a3092b..34b8f269976b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -423,6 +423,17 @@ enum plink_actions { PLINK_ACTION_BLOCK, }; +/** + * enum station_parameters_apply_mask - station parameter values to apply + * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) + * + * Not all station parameters have in-band "no change" signalling, + * for those that don't these flags will are used. + */ +enum station_parameters_apply_mask { + STATION_PARAM_APPLY_UAPSD = BIT(0), +}; + /** * struct station_parameters - station parameters * @@ -450,6 +461,7 @@ struct station_parameters { u8 *supported_rates; struct net_device *vlan; u32 sta_flags_mask, sta_flags_set; + u32 sta_modify_mask; int listen_interval; u16 aid; u8 supported_rates_len; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8fef3cddbc4f..13061ebc93ef 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -714,8 +714,10 @@ static void sta_apply_parameters(struct ieee80211_local *local, } spin_unlock_irqrestore(&sta->flaglock, flags); - sta->sta.uapsd_queues = params->uapsd_queues; - sta->sta.max_sp = params->max_sp; + if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { + sta->sta.uapsd_queues = params->uapsd_queues; + sta->sta.max_sp = params->max_sp; + } /* * cfg80211 validates this (1-2007) and allows setting the AID diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b85075761e24..3799623e7f46 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2643,6 +2643,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) return -EINVAL; + + params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && From 90826313fd69d198da7574779460f793765abfa5 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 28 Sep 2011 16:56:10 +0530 Subject: [PATCH 118/155] ath9k/ath9k_htc: Fix PS wrappers for RF kill ath9k_hw_gpio_get reads the GPIO in/out registers to get the status of GPIO pins, so use PS wrappers Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 7 ++++++- drivers/net/wireless/ath/ath9k/htc_drv_gpio.c | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index afbf5400a52a..fd0f84ebdb51 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -84,9 +84,14 @@ void ath_init_leds(struct ath_softc *sc) static bool ath_is_rfkill_set(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + bool is_blocked; - return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == + ath9k_ps_wakeup(sc); + is_blocked = ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == ah->rfkill_polarity; + ath9k_ps_restore(sc); + + return is_blocked; } void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index db2352e5cc0d..e3a02eb8e0cc 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -228,8 +228,14 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv) static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) { - return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == - priv->ah->rfkill_polarity; + bool is_blocked; + + ath9k_htc_ps_wakeup(priv); + is_blocked = ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == + priv->ah->rfkill_polarity; + ath9k_htc_ps_restore(priv); + + return is_blocked; } void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) From a76011e2cbb6915f60488477311e0f269cee6496 Mon Sep 17 00:00:00 2001 From: Greg Dietsche Date: Wed, 28 Sep 2011 17:54:04 -0500 Subject: [PATCH 119/155] iwlagn: iwl-agn-rs: remove unnecessary null check for sta and lq_sta both sta and lq_sta are guaranteed to be not null in the calling function so we don't need to check them here. Signed-off-by: Greg Dietsche Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index c14f8d6fd7d8..7d6a3bf64950 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -2273,9 +2273,6 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, info->flags & IEEE80211_TX_CTL_NO_ACK) return; - if (!sta || !lq_sta) - return; - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; tid = rs_tl_add_packet(lq_sta, hdr); From 109086ce0b0f94760bdb0e8e2566ff8a2d673639 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:50 +0300 Subject: [PATCH 120/155] nl80211: support sending TDLS commands/frames Add support for sending high-level TDLS commands and TDLS frames via NL80211_CMD_TDLS_OPER and NL80211_CMD_TDLS_MGMT, respectively. Add appropriate cfg80211 callbacks for lower level drivers. Add wiphy capability flags for TDLS support and advertise them via nl80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 42 +++++++++++++++++++++ include/net/cfg80211.h | 17 +++++++++ net/wireless/nl80211.c | 81 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c73582fb9d20..a5ab23df5b17 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -506,6 +506,9 @@ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace * of PMKSA caching dandidates. * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -632,6 +635,9 @@ enum nl80211_commands { NL80211_CMD_PMKSA_CANDIDATE, + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1089,6 +1095,20 @@ enum nl80211_commands { * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and * %NL80211_CMD_FRAME commands. * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1311,6 +1331,12 @@ enum nl80211_attrs { NL80211_ATTR_TX_NO_CCK_RATE, + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2604,4 +2630,20 @@ enum nl80211_pmksa_candidate_attr { MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 }; +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 34b8f269976b..74f4f85be32f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1422,6 +1422,9 @@ struct cfg80211_gtk_rekey_data { * @set_ringparam: Set tx and rx ring sizes. * * @get_ringparam: Get tx and rx ring current and maximum sizes. + * + * @tdls_mgmt: Transmit a TDLS management frame. + * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1605,6 +1608,12 @@ struct cfg80211_ops { int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data); + + int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper); }; /* @@ -1657,6 +1666,12 @@ struct cfg80211_ops { * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the * firmware. * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP. + * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation. + * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z) + * link setup/discovery operations internally. Setup, discovery and + * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT + * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be + * used for asking the driver/firmware to perform a TDLS operation. */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1673,6 +1688,8 @@ enum wiphy_flags { WIPHY_FLAG_ENFORCE_COMBINATIONS = BIT(12), WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), WIPHY_FLAG_AP_UAPSD = BIT(14), + WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), + WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3799623e7f46..25a37fc951e3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -192,6 +192,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, + [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -732,9 +737,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); + if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) + NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * dev->wiphy.n_cipher_suites, @@ -877,6 +885,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } CMD(set_channel, SET_CHANNEL); CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); @@ -4966,6 +4978,57 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) return rdev->ops->flush_pmksa(&rdev->wiphy, dev); } +static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u8 action_code, dialog_token; + u16 status_code; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_mgmt) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || + !info->attrs[NL80211_ATTR_STATUS_CODE] || + !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || + !info->attrs[NL80211_ATTR_IE] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); + status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + + return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, + dialog_token, status_code, + nla_data(info->attrs[NL80211_ATTR_IE]), + nla_len(info->attrs[NL80211_ATTR_IE])); +} + +static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + enum nl80211_tdls_operation operation; + u8 *peer; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !rdev->ops->tdls_oper) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || + !info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); + peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + + return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); +} + static int nl80211_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { @@ -6281,6 +6344,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_TDLS_MGMT, + .doit = nl80211_tdls_mgmt, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_OPER, + .doit = nl80211_tdls_oper, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { From 768db3438b4b48a33d073093bb364e624409cab7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:51 +0300 Subject: [PATCH 121/155] mac80211: standardize adding supported rates IEs Relocate the mesh implementation of adding the (extended) supported rates IE to util.c, anticipating its use by other parts of mac80211. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ++++ net/mac80211/mesh.c | 58 --------------------------------------- net/mac80211/mesh.h | 4 --- net/mac80211/mesh_plink.c | 4 +-- net/mac80211/tx.c | 4 +-- net/mac80211/util.c | 57 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 66 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1e83afae3c64..b5f7ada2f87b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3444,4 +3444,9 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, int rssi_max_thold); void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb); + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, + struct sk_buff *skb); #endif /* MAC80211_H */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a4225ae69681..a7078fdba8ca 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -320,64 +320,6 @@ mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } -int -mesh_add_srates_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, rates, *pos; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - rates = sband->n_bitrates; - if (rates > 8) - rates = 8; - - if (skb_tailroom(skb) < rates + 2) - return -ENOMEM; - - pos = skb_put(skb, rates + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = rates; - for (i = 0; i < rates; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - - return 0; -} - -int -mesh_add_ext_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; - int rate; - u8 i, exrates, *pos; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - exrates = sband->n_bitrates; - if (exrates > 8) - exrates -= 8; - else - exrates = 0; - - if (skb_tailroom(skb) < exrates + 2) - return -ENOMEM; - - if (exrates) { - pos = skb_put(skb, exrates + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = exrates; - for (i = 8; i < sband->n_bitrates; i++) { - rate = sband->bitrates[i].bitrate; - *pos++ = (u8) (rate / 5); - } - } - return 0; -} - int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 7118e8e8855c..8c00e2d1d636 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -210,10 +210,6 @@ int mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); -int mesh_add_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_ext_srates_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 1213a23ff0fa..9cc5029b3c46 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -185,8 +185,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - if (mesh_add_srates_ie(skb, sdata) || - mesh_add_ext_srates_ie(skb, sdata) || + if (ieee80211_add_srates_ie(&sdata->vif, skb) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7cd6c28968b2..542272acfc1a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2307,9 +2307,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - if (mesh_add_srates_ie(skb, sdata) || + if (ieee80211_add_srates_ie(&sdata->vif, skb) || mesh_add_ds_params_ie(skb, sdata) || - mesh_add_ext_srates_ie(skb, sdata) || + ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2c9dc360dc6d..9d4f14621bb0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1364,3 +1364,60 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) _ieee80211_enable_rssi_reports(sdata, 0, 0); } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); + +int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, rates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + rates = sband->n_bitrates; + if (rates > 8) + rates = 8; + + if (skb_tailroom(skb) < rates + 2) + return -ENOMEM; + + pos = skb_put(skb, rates + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + for (i = 0; i < rates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + + return 0; +} + +int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + int rate; + u8 i, exrates, *pos; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + exrates = sband->n_bitrates; + if (exrates > 8) + exrates -= 8; + else + exrates = 0; + + if (skb_tailroom(skb) < exrates + 2) + return -ENOMEM; + + if (exrates) { + pos = skb_put(skb, exrates + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = exrates; + for (i = 8; i < sband->n_bitrates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + } + return 0; +} From dfe018bf99537e42c816d3f543620a7e09fcf3cd Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:52 +0300 Subject: [PATCH 122/155] mac80211: handle TDLS high-level commands and frames Register and implement the TDLS cfg80211 callback functions. Internally prepare and send TDLS management frames. We incorporate local STA capabilities and supported rates with extra IEs given by usermode. The resulting packet is either encapsulated in a data frame, or assembled as an action frame. It is transmitted either directly or through the AP, as mandated by the TDLS specification. Declare support for the TDLS external setup wiphy capability. This tells usermode to handle link setup and discovery on its own, and use the kernel driver for sending TDLS mgmt packets. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 85 +++++++++++ include/linux/if_ether.h | 1 + net/mac80211/Kconfig | 12 ++ net/mac80211/cfg.c | 310 ++++++++++++++++++++++++++++++++++++++ net/mac80211/main.c | 4 + 5 files changed, 412 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b5e0a5c344fd..48363c3c40f8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -759,6 +759,12 @@ struct ieee80211_mgmt { u8 action; u8 smps_control; } __attribute__ ((packed)) ht_smps; + struct { + u8 action_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed tdls_discover_resp; } u; } __attribute__ ((packed)) action; } u; @@ -805,6 +811,52 @@ struct ieee80211_pspoll { u8 ta[6]; } __attribute__ ((packed)); +/* TDLS */ + +/* Link-id information element */ +struct ieee80211_tdls_lnkie { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[6]; + u8 init_sta[6]; + u8 resp_sta[6]; +} __packed; + +struct ieee80211_tdls_data { + u8 da[6]; + u8 sa[6]; + __be16 ether_type; + u8 payload_type; + u8 category; + u8 action_code; + union { + struct { + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_req; + struct { + __le16 status_code; + u8 dialog_token; + __le16 capability; + u8 variable[0]; + } __packed setup_resp; + struct { + __le16 status_code; + u8 dialog_token; + u8 variable[0]; + } __packed setup_cfm; + struct { + __le16 reason_code; + u8 variable[0]; + } __packed teardown; + struct { + u8 dialog_token; + u8 variable[0]; + } __packed discover_req; + } u; +} __packed; + /** * struct ieee80211_bar - HT Block Ack Request * @@ -1196,6 +1248,8 @@ enum ieee80211_eid { WLAN_EID_TS_DELAY = 43, WLAN_EID_TCLAS_PROCESSING = 44, WLAN_EID_QOS_CAPA = 46, + /* 802.11z */ + WLAN_EID_LINK_ID = 101, /* 802.11s */ WLAN_EID_MESH_CONFIG = 113, WLAN_EID_MESH_ID = 114, @@ -1279,6 +1333,7 @@ enum ieee80211_category { WLAN_CATEGORY_HT = 7, WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, + WLAN_CATEGORY_TDLS = 12, WLAN_CATEGORY_MESH_ACTION = 13, WLAN_CATEGORY_MULTIHOP_ACTION = 14, WLAN_CATEGORY_SELF_PROTECTED = 15, @@ -1342,6 +1397,36 @@ enum ieee80211_key_len { WLAN_KEY_LEN_AES_CMAC = 16, }; +/* Public action codes */ +enum ieee80211_pub_actioncode { + WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14, +}; + +/* TDLS action codes */ +enum ieee80211_tdls_actioncode { + WLAN_TDLS_SETUP_REQUEST = 0, + WLAN_TDLS_SETUP_RESPONSE = 1, + WLAN_TDLS_SETUP_CONFIRM = 2, + WLAN_TDLS_TEARDOWN = 3, + WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, + WLAN_TDLS_PEER_PSM_REQUEST = 7, + WLAN_TDLS_PEER_PSM_RESPONSE = 8, + WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, + WLAN_TDLS_DISCOVERY_REQUEST = 10, +}; + +/* + * TDLS capabililites to be enabled in the 5th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) +#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) + +/* TDLS specific payload type in the LLC/SNAP header */ +#define WLAN_TDLS_SNAP_RFTYPE 0x2 + /** * enum - mesh path selection protocol identifier * diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index a3d99ff6e3b5..49c38fc8dbc3 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -83,6 +83,7 @@ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ #define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index d1886b59bec4..7d3b438755f0 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -225,6 +225,18 @@ config MAC80211_VERBOSE_MHWMP_DEBUG Do not select this option. +config MAC80211_VERBOSE_TDLS_DEBUG + bool "Verbose TDLS debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out very + verbose TDLS selection debugging messages (when mac80211 + is a TDLS STA). + It should not be selected on production systems as those + messages are remotely triggerable. + + Do not select this option. + config MAC80211_DEBUG_COUNTERS bool "Extra statistics for TX/RX debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 13061ebc93ef..1d17677a0ec1 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -2128,6 +2129,313 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, return 0; } +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 7); + + *pos++ = WLAN_EID_EXT_CAPABILITY; + *pos++ = 5; /* len */ + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = 0x0; + *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u16 capab; + + capab = 0; + if (local->oper_channel->band != IEEE80211_BAND_2GHZ) + return capab; + + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, + u8 *peer, u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_tdls_data *tf; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + break; + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_mgmt *mgmt; + + mgmt = (void *)skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + + ieee80211_add_srates_ie(&sdata->vif, skb); + ieee80211_add_ext_srates_ie(&sdata->vif, skb); + ieee80211_tdls_add_ext_capab(skb); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info; + struct sk_buff *skb = NULL; + bool send_direct; + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in managed mode, and associated */ + if (sdata->vif.type != NL80211_IFTYPE_STATION || + !sdata->u.mgd.associated) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS mgmt action %d peer %pM\n", action_code, peer); +#endif + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 7 + /* ext capab */ + extra_ies_len + + sizeof(struct ieee80211_tdls_lnkie)); + if (!skb) + return -ENOMEM; + + info = IEEE80211_SKB_CB(skb); + skb_reserve(skb, local->hw.extra_tx_headroom); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, + action_code, dialog_token, + status_code, skb); + send_direct = false; + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, + dialog_token, status_code, + skb); + send_direct = true; + break; + default: + ret = -ENOTSUPP; + break; + } + + if (ret < 0) + goto fail; + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + /* we are the initiator */ + ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, + sdata->u.mgd.bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + /* we are the responder */ + ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, + sdata->u.mgd.bssid); + break; + default: + ret = -ENOTSUPP; + goto fail; + } + + if (send_direct) { + ieee80211_tx_skb(sdata, skb); + return 0; + } + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb_set_queue_mapping(skb, IEEE80211_AC_BK); + skb->priority = 2; + break; + default: + skb_set_queue_mapping(skb, IEEE80211_AC_VI); + skb->priority = 5; + break; + } + + /* disable bottom halves when entering the Tx path */ + local_bh_disable(); + ret = ieee80211_subif_start_xmit(skb, dev); + local_bh_enable(); + + return ret; + +fail: + dev_kfree_skb(skb); + return ret; +} + +static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + u8 *peer, enum nl80211_tdls_operation oper) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EINVAL; + +#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG + printk(KERN_DEBUG "TDLS oper %d peer %pM\n", oper, peer); +#endif + + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + break; + case NL80211_TDLS_DISABLE_LINK: + return sta_info_destroy_addr(sdata, peer); + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + /* We don't support in-driver setup/teardown/discovery */ + return -ENOTSUPP; + default: + return -ENOTSUPP; + } + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -2191,4 +2499,6 @@ struct cfg80211_ops mac80211_config_ops = { .set_ringparam = ieee80211_set_ringparam, .get_ringparam = ieee80211_get_ringparam, .set_rekey_data = ieee80211_set_rekey_data, + .tdls_oper = ieee80211_tdls_oper, + .tdls_mgmt = ieee80211_tdls_mgmt, }; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a5809a1a6239..336ceb9d2462 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -863,6 +863,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->ops->sched_scan_start) local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + /* mac80211 based drivers don't support internal TDLS setup */ + if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) + local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + result = wiphy_register(local->hw.wiphy); if (result < 0) goto fail_wiphy_register; From 07ba55d7f1d0da174c9bc545c713b44cee760197 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:53 +0300 Subject: [PATCH 123/155] nl80211/mac80211: allow adding TDLS peers as stations When adding a TDLS peer STA, mark it with a new flag in both nl80211 and mac80211. Before adding a peer, make sure the wiphy supports TDLS and our operating mode is appropriate (managed). In addition, make sure all peers are removed on disassociation. A TDLS peer is first added just before link setup is initiated. In later setup stages we have more info about peer supported rates, capabilities, etc. This info is reported via nl80211_set_station(). Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ net/mac80211/cfg.c | 20 ++++++++++++++++++++ net/mac80211/mlme.c | 7 ++++--- net/mac80211/sta_info.h | 2 ++ net/wireless/nl80211.c | 26 ++++++++++++++++++++++---- 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index a5ab23df5b17..9d797f253d8e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1434,6 +1434,7 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1444,6 +1445,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1d17677a0ec1..119a573af14b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -713,6 +713,12 @@ static void sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) sta->flags |= WLAN_STA_AUTH; } + + if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + sta->flags &= ~WLAN_STA_TDLS_PEER; + if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + sta->flags |= WLAN_STA_TDLS_PEER; + } spin_unlock_irqrestore(&sta->flaglock, flags); if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { @@ -813,6 +819,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, sta_apply_parameters(local, sta, params); + /* Only TDLS-supporting stations can add TDLS peers */ + if ((sta->flags & WLAN_STA_TDLS_PEER) && + !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && + sdata->vif.type == NL80211_IFTYPE_STATION)) + return -ENOTSUPP; + rate_control_rate_init(sta); layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || @@ -865,6 +877,14 @@ static int ieee80211_change_station(struct wiphy *wiphy, return -ENOENT; } + /* The TDLS bit cannot be toggled after the STA was added */ + if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != + !!test_sta_flags(sta, WLAN_STA_TDLS_PEER)) { + rcu_read_unlock(); + return -EINVAL; + } + if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cd37a4e3c0d7..b98c43a7f191 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1137,8 +1137,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); + /* remove AP and TDLS peers */ if (remove_sta) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(local, sdata); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); @@ -2738,7 +2739,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, cookie, !req->local_state_change); if (assoc_bss) - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); @@ -2778,7 +2779,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, cookie, !req->local_state_change); - sta_info_destroy_addr(sdata, bssid); + sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 56a3d38a2cd1..b6bd4e9d8722 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -45,6 +45,7 @@ * station in power-save mode, reply when the driver unblocks. * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal * buffers. Automatically cleared on station wake-up. + * @WLAN_STA_TDLS_PEER: station is a TDLS peer. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -61,6 +62,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, WLAN_STA_PS_DRIVER_BUF = 1<<14, + WLAN_STA_TDLS_PEER = 1<<15, }; #define STA_TID_NUM 16 diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 25a37fc951e3..edf655aeea00 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2530,18 +2530,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - /* disallow everything but AUTHORIZED flag */ + /* disallow things sta doesn't support */ if (params.plink_action) err = -EINVAL; if (params.vlan) err = -EINVAL; - if (params.supported_rates) + if (params.supported_rates && + !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) err = -EINVAL; if (params.ht_capa) err = -EINVAL; if (params.listen_interval >= 0) err = -EINVAL; - if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) + if (params.sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_TDLS_PEER))) + err = -EINVAL; + /* can't change the TDLS bit */ + if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) err = -EINVAL; break; case NL80211_IFTYPE_MESH_POINT: @@ -2662,7 +2669,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + /* + * Only managed stations can add TDLS peers, and only when the + * wiphy supports external TDLS setup. + */ + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION && + !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && + (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))) return -EINVAL; err = get_vlan(info, rdev, ¶ms.vlan); From 941c93cd039852b7ab02c74f4698c99d82bd6cfe Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Sep 2011 14:12:54 +0300 Subject: [PATCH 124/155] mac80211: data path modification for TDLS peers Mark the STA entries of enabled TDLS peers with a new "peer authorized" flag. During link setup, allow special TDLS setup frames through the AP, but otherwise drop all packets destined to the peer. This is required by the TDLS (802.11z) specification in order to prevent reordering of MSDUs between the AP and direct paths. When setup completes and the peer is authorized, send data directly, bypassing the AP. In the Rx path, allow data to be received directly from TDLS peers. Signed-off-by: Arik Nemtsov Cc: Kalyan C Gaddam Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 10 ++++++++++ net/mac80211/sta_info.h | 5 ++++- net/mac80211/tx.c | 42 +++++++++++++++++++++++++++++++++++++---- net/wireless/util.c | 5 +++-- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 119a573af14b..bdf9852eec5b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2427,6 +2427,7 @@ fail: static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper) { + struct sta_info *sta; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) @@ -2441,6 +2442,15 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, switch (oper) { case NL80211_TDLS_ENABLE_LINK: + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (!sta) { + rcu_read_unlock(); + return -ENOLINK; + } + + set_sta_flags(sta, WLAN_STA_TDLS_PEER_AUTH); + rcu_read_unlock(); break; case NL80211_TDLS_DISABLE_LINK: return sta_info_destroy_addr(sdata, peer); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b6bd4e9d8722..c10e2e8632b5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -45,7 +45,9 @@ * station in power-save mode, reply when the driver unblocks. * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal * buffers. Automatically cleared on station wake-up. - * @WLAN_STA_TDLS_PEER: station is a TDLS peer. + * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. + * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct + * packets. This means the link is enabled. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -63,6 +65,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL = 1<<13, WLAN_STA_PS_DRIVER_BUF = 1<<14, WLAN_STA_TDLS_PEER = 1<<15, + WLAN_STA_TDLS_PEER_AUTH = 1<<16, }; #define STA_TID_NUM 16 diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 542272acfc1a..0ca16880bbb4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1726,6 +1726,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct sta_info *sta = NULL; u32 sta_flags = 0; struct sk_buff *tmp_skb; + bool tdls_direct = false; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1837,11 +1838,43 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, break; #endif case NL80211_IFTYPE_STATION: - memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); - if (sdata->u.mgd.use_4addr && - cpu_to_be16(ethertype) != sdata->control_port_protocol) { - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); + if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { + rcu_read_lock(); + sta = sta_info_get(sdata, skb->data); + if (sta) + sta_flags = get_sta_flags(sta); + rcu_read_unlock(); + + /* + * If the TDLS link is enabled, send everything + * directly. Otherwise, allow TDLS setup frames + * to be transmitted indirectly. + */ + tdls_direct = + (sta_flags & WLAN_STA_TDLS_PEER) && + ((sta_flags & WLAN_STA_TDLS_PEER_AUTH) || + !(ethertype == ETH_P_TDLS && skb->len > 14 && + skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); + } + + if (tdls_direct) { + /* link during setup - throw out frames to peer */ + if (!(sta_flags & WLAN_STA_TDLS_PEER_AUTH)) { + ret = NETDEV_TX_OK; + goto fail; + } + + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); + hdrlen = 24; + } else if (sdata->u.mgd.use_4addr && + cpu_to_be16(ethertype) != sdata->control_port_protocol) { + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); /* RA TA DA SA */ + memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); @@ -1849,6 +1882,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, } else { fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ + memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; diff --git a/net/wireless/util.c b/net/wireless/util.c index 6304ed63588a..2f178f73943f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -396,8 +396,9 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, } break; case cpu_to_le16(0): - if (iftype != NL80211_IFTYPE_ADHOC) - return -1; + if (iftype != NL80211_IFTYPE_ADHOC && + iftype != NL80211_IFTYPE_STATION) + return -1; break; } From 042ec4533720122e6cb93dd9f3b6a75fe2fcff16 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:26 +0200 Subject: [PATCH 125/155] mac80211: let drivers inform it about per TID buffered frames For uAPSD implementation, it is necessary to know on which ACs frames are buffered. mac80211 obviously knows about the frames it has buffered itself, but with aggregation many drivers buffer frames. Thus, mac80211 needs to be informed about this. For now, since we don't have APSD in any form, this will unconditionally set the TIM bit for the station but later with uAPSD only some ACs might cause the TIM bit to be set. ath9k is the only driver using this API and I only modify it in the most basic way, it won't be able to implement uAPSD with this yet. But it can't do that anyway since there's no way to selectively release frames to the peer yet. Since drivers will buffer frames per TID, let them inform mac80211 on a per TID basis, mac80211 will then sort out the AC mapping itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 +-- drivers/net/wireless/ath/ath9k/xmit.c | 14 ++++++------ include/net/mac80211.h | 30 ++++++++++++++++++++------ net/mac80211/sta_info.c | 8 +++++-- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 94d887b65e69..1e8614783181 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -340,7 +340,8 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); -bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an); +void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, + struct ath_node *an); /********/ /* VIFs */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index edaa7843bf4c..0ebf7321df12 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1833,8 +1833,7 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, switch (cmd) { case STA_NOTIFY_SLEEP: an->sleeping = true; - if (ath_tx_aggr_sleep(sc, an)) - ieee80211_sta_set_tim(sta); + ath_tx_aggr_sleep(sta, sc, an); break; case STA_NOTIFY_AWAKE: an->sleeping = false; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index f5d4764888b9..c2bfc57958d8 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -542,7 +542,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, /* prepend un-acked frames to the beginning of the pending frame queue */ if (!skb_queue_empty(&bf_pending)) { if (an->sleeping) - ieee80211_sta_set_tim(sta); + ieee80211_sta_set_buffered(sta, tid->tidno, true); spin_lock_bh(&txq->axq_lock); if (clear_filter) @@ -1153,12 +1153,13 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) ath_tx_flush_tid(sc, txtid); } -bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) +void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, + struct ath_node *an) { struct ath_atx_tid *tid; struct ath_atx_ac *ac; struct ath_txq *txq; - bool buffered = false; + bool buffered; int tidno; for (tidno = 0, tid = &an->tid[tidno]; @@ -1172,8 +1173,7 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) spin_lock_bh(&txq->axq_lock); - if (!skb_queue_empty(&tid->buf_q)) - buffered = true; + buffered = !skb_queue_empty(&tid->buf_q); tid->sched = false; list_del(&tid->list); @@ -1184,9 +1184,9 @@ bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an) } spin_unlock_bh(&txq->axq_lock); - } - return buffered; + ieee80211_sta_set_buffered(sta, tidno, buffered); + } } void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b5f7ada2f87b..e66638e749c6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2361,17 +2361,35 @@ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, #define IEEE80211_TX_STATUS_HEADROOM 13 /** - * ieee80211_sta_set_tim - set the TIM bit for a sleeping station + * ieee80211_sta_set_buffered - inform mac80211 about driver-buffered frames * @sta: &struct ieee80211_sta pointer for the sleeping station + * @tid: the TID that has buffered frames + * @buffered: indicates whether or not frames are buffered for this TID * * If a driver buffers frames for a powersave station instead of passing - * them back to mac80211 for retransmission, the station needs to be told - * to wake up using the TIM bitmap in the beacon. + * them back to mac80211 for retransmission, the station may still need + * to be told that there are buffered frames via the TIM bit. * - * This function sets the station's TIM bit - it will be cleared when the - * station wakes up. + * This function informs mac80211 whether or not there are frames that are + * buffered in the driver for a given TID; mac80211 can then use this data + * to set the TIM bit (NOTE: This may call back into the driver's set_tim + * call! Beware of the locking!) + * + * If all frames are released to the station (due to PS-poll or uAPSD) + * then the driver needs to inform mac80211 that there no longer are + * frames buffered. However, when the station wakes up mac80211 assumes + * that all buffered frames will be transmitted and clears this data, + * drivers need to make sure they inform mac80211 about all buffered + * frames on the sleep transition (sta_notify() with %STA_NOTIFY_SLEEP). + * + * Note that technically mac80211 only needs to know this per AC, not per + * TID, but since driver buffering will inevitably happen per TID (since + * it is related to aggregation) it is easier to make mac80211 map the + * TID to the AC as required instead of keeping track in all drivers that + * use this API. */ -void ieee80211_sta_set_tim(struct ieee80211_sta *sta); +void ieee80211_sta_set_buffered(struct ieee80211_sta *sta, + u8 tid, bool buffered); /** * ieee80211_tx_status - transmit status callback diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c52e58c0a979..016742d4c48e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1117,11 +1117,15 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta) +void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, + u8 tid, bool buffered) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + if (!buffered) + return; + set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); sta_info_set_tim_bit(sta); } -EXPORT_SYMBOL(ieee80211_sta_set_tim); +EXPORT_SYMBOL(ieee80211_sta_set_buffered); From c868cb35d013896ab6a80a554fb88baef06cedcd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:27 +0200 Subject: [PATCH 126/155] mac80211: unify TIM bit handling Currently, the TIM bit for a given station is set and cleared all over the place. Since the logic to set/clear it will become much more complex when we add uAPSD support, as a first step let's collect the entire logic in one place. This requires a few small adjustments to other places. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/sta_info.c | 101 +++++++++++++++++----------------------- net/mac80211/sta_info.h | 3 +- net/mac80211/status.c | 1 + net/mac80211/tx.c | 15 +++--- 4 files changed, 51 insertions(+), 69 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 016742d4c48e..863d59fe6886 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -641,54 +641,42 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) bss->tim[aid / 8] &= ~(1 << (aid % 8)); } -static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) -{ - BUG_ON(!bss); - - __bss_tim_set(bss, sta->sta.aid); - - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, true); - sta->local->tim_in_locked_section = false; - } -} - -void sta_info_set_tim_bit(struct sta_info *sta) +void sta_info_recalc_tim(struct sta_info *sta) { + struct ieee80211_local *local = sta->local; + struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; + bool have_data = false; - BUG_ON(!sta->sdata->bss); + if (WARN_ON_ONCE(!sta->sdata->bss)) + return; - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_set_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); -} + /* No need to do anything if the driver does all */ + if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return; -static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, - struct sta_info *sta) -{ - BUG_ON(!bss); + if (sta->dead) + goto done; - __bss_tim_clear(bss, sta->sta.aid); + have_data = test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF) || + !skb_queue_empty(&sta->tx_filtered) || + !skb_queue_empty(&sta->ps_tx_buf); - if (sta->local->ops->set_tim) { - sta->local->tim_in_locked_section = true; - drv_set_tim(sta->local, &sta->sta, false); - sta->local->tim_in_locked_section = false; + done: + spin_lock_irqsave(&local->sta_lock, flags); + + if (have_data) + __bss_tim_set(bss, sta->sta.aid); + else + __bss_tim_clear(bss, sta->sta.aid); + + if (local->ops->set_tim) { + local->tim_in_locked_section = true; + drv_set_tim(local, &sta->sta, have_data); + local->tim_in_locked_section = false; } -} -void sta_info_clear_tim_bit(struct sta_info *sta) -{ - unsigned long flags; - - BUG_ON(!sta->sdata->bss); - - spin_lock_irqsave(&sta->local->sta_lock, flags); - __sta_info_clear_tim_bit(sta->sdata->bss, sta); - spin_unlock_irqrestore(&sta->local->sta_lock, flags); + spin_unlock_irqrestore(&local->sta_lock, flags); } static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) @@ -717,6 +705,10 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, unsigned long flags; struct sk_buff *skb; + /* This is only necessary for stations on BSS interfaces */ + if (!sta->sdata->bss) + return false; + for (;;) { spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); skb = skb_peek(&sta->ps_tx_buf); @@ -736,9 +728,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, #endif dev_kfree_skb(skb); - if (skb_queue_empty(&sta->ps_tx_buf) && - !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF)) - sta_info_clear_tim_bit(sta); + /* if the queue is now empty recalc TIM bit */ + if (skb_queue_empty(&sta->ps_tx_buf)) + sta_info_recalc_tim(sta); } return !skb_queue_empty(&sta->ps_tx_buf); @@ -748,7 +740,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; - struct sk_buff *skb; unsigned long flags; int ret, i; @@ -792,7 +783,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) BUG_ON(!sdata->bss); atomic_dec(&sdata->bss->num_sta_ps); - sta_info_clear_tim_bit(sta); + sta_info_recalc_tim(sta); } local->num_sta--; @@ -818,6 +809,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf); + __skb_queue_purge(&sta->ps_tx_buf); + __skb_queue_purge(&sta->tx_filtered); + #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); @@ -840,14 +835,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) } #endif - while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { - local->total_ps_buffered--; - dev_kfree_skb_any(skb); - } - - while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) - dev_kfree_skb_any(skb); - __sta_info_free(local, sta); return 0; @@ -1027,9 +1014,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); - if (!skb_queue_empty(&sta->ps_tx_buf)) - sta_info_clear_tim_bit(sta); - /* Send all buffered frames to the station */ sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, @@ -1037,6 +1021,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) sent += buffered; local->total_ps_buffered -= buffered; + sta_info_recalc_tim(sta); + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " "since STA not sleeping anymore\n", sdata->name, @@ -1086,8 +1072,7 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) ieee80211_add_pending_skb(local, skb); - if (no_pending_pkts) - sta_info_clear_tim_bit(sta); + sta_info_recalc_tim(sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG } else { /* @@ -1126,6 +1111,6 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, return; set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); - sta_info_set_tim_bit(sta); + sta_info_recalc_tim(sta); } EXPORT_SYMBOL(ieee80211_sta_set_buffered); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c10e2e8632b5..c9ffb7ce636b 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -528,8 +528,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); -void sta_info_set_tim_bit(struct sta_info *sta); -void sta_info_clear_tim_bit(struct sta_info *sta); +void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index d50358c45ab0..8354dcb0e1e3 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -106,6 +106,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, if (test_sta_flags(sta, WLAN_STA_PS_STA) && skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { skb_queue_tail(&sta->tx_filtered, skb); + sta_info_recalc_tim(sta); return; } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0ca16880bbb4..d6754908ff79 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -469,15 +469,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) } else tx->local->total_ps_buffered++; - /* - * Queue frame to be sent after STA wakes up/polls, - * but don't set the TIM bit if the driver is blocking - * wakeup or poll response transmissions anyway. - */ - if (skb_queue_empty(&sta->ps_tx_buf) && - !(staflags & WLAN_STA_PS_DRIVER)) - sta_info_set_tim_bit(sta); - info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; @@ -488,6 +479,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); + /* + * We queued up some frames, so the TIM bit might + * need to be set, recalculate it. + */ + sta_info_recalc_tim(sta); + return TX_QUEUED; } #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG From 60750397122fe0fb81a6e52fd790b3f749b6e010 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:28 +0200 Subject: [PATCH 127/155] mac80211: also expire filtered frames mac80211 will expire normal PS-buffered frames, but if the device rejected some frames for a sleeping station, these won't be on the ps_tx_buf queue but on the tx_filtered queue instead; this is done to avoid reordering. However, mac80211 will not expire frames from the filtered queue, let's fix that. Also add a more comments to what all this expiry is doing and how it works. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/sta_info.c | 57 +++++++++++++++++++++++++++++++++++++---- net/mac80211/status.c | 5 ++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 863d59fe6886..8dabe66fc37f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -709,6 +709,39 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, if (!sta->sdata->bss) return false; + /* + * First check for frames that should expire on the filtered + * queue. Frames here were rejected by the driver and are on + * a separate queue to avoid reordering with normal PS-buffered + * frames. They also aren't accounted for right now in the + * total_ps_buffered counter. + */ + for (;;) { + spin_lock_irqsave(&sta->tx_filtered.lock, flags); + skb = skb_peek(&sta->tx_filtered); + if (sta_info_buffer_expired(sta, skb)) + skb = __skb_dequeue(&sta->tx_filtered); + else + skb = NULL; + spin_unlock_irqrestore(&sta->tx_filtered.lock, flags); + + /* + * Frames are queued in order, so if this one + * hasn't expired yet we can stop testing. If + * we actually reached the end of the queue we + * also need to stop, of course. + */ + if (!skb) + break; + dev_kfree_skb(skb); + } + + /* + * Now also check the normal PS-buffered queue, this will + * only find something if the filtered queue was emptied + * since the filtered frames are all before the normal PS + * buffered frames. + */ for (;;) { spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); skb = skb_peek(&sta->ps_tx_buf); @@ -718,6 +751,11 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, skb = NULL; spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); + /* + * frames are queued in order, so if this one + * hasn't expired yet (or we reached the end of + * the queue) we can stop testing + */ if (!skb) break; @@ -727,13 +765,22 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, sta->sta.addr); #endif dev_kfree_skb(skb); - - /* if the queue is now empty recalc TIM bit */ - if (skb_queue_empty(&sta->ps_tx_buf)) - sta_info_recalc_tim(sta); } - return !skb_queue_empty(&sta->ps_tx_buf); + /* + * Finally, recalculate the TIM bit for this station -- it might + * now be clear because the station was too slow to retrieve its + * frames. + */ + sta_info_recalc_tim(sta); + + /* + * Return whether there are any frames still buffered, this is + * used to check whether the cleanup timer still needs to run, + * if there are no frames we don't need to rearm the timer. + */ + return !(skb_queue_empty(&sta->ps_tx_buf) && + skb_queue_empty(&sta->tx_filtered)); } static int __must_check __sta_info_destroy(struct sta_info *sta) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 8354dcb0e1e3..783542a8ea20 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -107,6 +107,11 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { skb_queue_tail(&sta->tx_filtered, skb); sta_info_recalc_tim(sta); + + if (!timer_pending(&local->sta_cleanup)) + mod_timer(&local->sta_cleanup, + round_jiffies(jiffies + + STA_INFO_CLEANUP_INTERVAL)); return; } From 948d887dec1042a7d78ae311908113e26502062f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:29 +0200 Subject: [PATCH 128/155] mac80211: split PS buffers into ACs For uAPSD support we'll need to have per-AC PS buffers. As this is a major undertaking, split the buffers before really adding support for uAPSD. This already makes some reference to the uapsd_queues variable, but for now that will never be non-zero. Since book-keeping is complicated, also change the logic for keeping a maximum of frames only and allow 64 frames per AC (up from 128 for a station). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 1 + net/mac80211/debugfs_sta.c | 10 +- net/mac80211/sta_info.c | 204 ++++++++++++++++++++++++++++--------- net/mac80211/sta_info.h | 24 ++--- net/mac80211/status.c | 17 +++- net/mac80211/tx.c | 42 ++++---- 6 files changed, 214 insertions(+), 84 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e66638e749c6..acf9eaf59641 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -109,6 +109,7 @@ enum ieee80211_ac_numbers { IEEE80211_AC_BE = 2, IEEE80211_AC_BK = 3, }; +#define IEEE80211_NUM_ACS 4 /** * struct ieee80211_tx_queue_params - transmit queue configuration diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a01d2137fddc..20ec2b0cb3c1 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -78,8 +78,14 @@ static ssize_t sta_num_ps_buf_frames_read(struct file *file, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; - return mac80211_format_buffer(userbuf, count, ppos, "%u\n", - skb_queue_len(&sta->ps_tx_buf)); + char buf[17*IEEE80211_NUM_ACS], *p = buf; + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac, + skb_queue_len(&sta->ps_tx_buf[ac]) + + skb_queue_len(&sta->tx_filtered[ac])); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } STA_OPS(num_ps_buf_frames); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8dabe66fc37f..4d85672f0b8f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -309,8 +309,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, */ sta->timer_to_tid[i] = i; } - skb_queue_head_init(&sta->ps_tx_buf); - skb_queue_head_init(&sta->tx_filtered); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + skb_queue_head_init(&sta->ps_tx_buf[i]); + skb_queue_head_init(&sta->tx_filtered[i]); + } for (i = 0; i < NUM_RX_DATA_QUEUES; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); @@ -641,12 +643,32 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) bss->tim[aid / 8] &= ~(1 << (aid % 8)); } +static unsigned long ieee80211_tids_for_ac(int ac) +{ + /* If we ever support TIDs > 7, this obviously needs to be adjusted */ + switch (ac) { + case IEEE80211_AC_VO: + return BIT(6) | BIT(7); + case IEEE80211_AC_VI: + return BIT(4) | BIT(5); + case IEEE80211_AC_BE: + return BIT(0) | BIT(3); + case IEEE80211_AC_BK: + return BIT(1) | BIT(2); + default: + WARN_ON(1); + return 0; + } +} + void sta_info_recalc_tim(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_if_ap *bss = sta->sdata->bss; unsigned long flags; - bool have_data = false; + bool indicate_tim = false; + u8 ignore_for_tim = sta->sta.uapsd_queues; + int ac; if (WARN_ON_ONCE(!sta->sdata->bss)) return; @@ -658,21 +680,43 @@ void sta_info_recalc_tim(struct sta_info *sta) if (sta->dead) goto done; - have_data = test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF) || - !skb_queue_empty(&sta->tx_filtered) || - !skb_queue_empty(&sta->ps_tx_buf); + /* + * If all ACs are delivery-enabled then we should build + * the TIM bit for all ACs anyway; if only some are then + * we ignore those and build the TIM bit using only the + * non-enabled ones. + */ + if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_tim = 0; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + + if (ignore_for_tim & BIT(ac)) + continue; + + indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac]); + if (indicate_tim) + break; + + tids = ieee80211_tids_for_ac(ac); + + indicate_tim |= + sta->driver_buffered_tids & tids; + } done: spin_lock_irqsave(&local->sta_lock, flags); - if (have_data) + if (indicate_tim) __bss_tim_set(bss, sta->sta.aid); else __bss_tim_clear(bss, sta->sta.aid); if (local->ops->set_tim) { local->tim_in_locked_section = true; - drv_set_tim(local, &sta->sta, have_data); + drv_set_tim(local, &sta->sta, indicate_tim); local->tim_in_locked_section = false; } @@ -699,16 +743,12 @@ static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) } -static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, - struct sta_info *sta) +static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, + struct sta_info *sta, int ac) { unsigned long flags; struct sk_buff *skb; - /* This is only necessary for stations on BSS interfaces */ - if (!sta->sdata->bss) - return false; - /* * First check for frames that should expire on the filtered * queue. Frames here were rejected by the driver and are on @@ -717,13 +757,13 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * total_ps_buffered counter. */ for (;;) { - spin_lock_irqsave(&sta->tx_filtered.lock, flags); - skb = skb_peek(&sta->tx_filtered); + spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); + skb = skb_peek(&sta->tx_filtered[ac]); if (sta_info_buffer_expired(sta, skb)) - skb = __skb_dequeue(&sta->tx_filtered); + skb = __skb_dequeue(&sta->tx_filtered[ac]); else skb = NULL; - spin_unlock_irqrestore(&sta->tx_filtered.lock, flags); + spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); /* * Frames are queued in order, so if this one @@ -743,13 +783,13 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * buffered frames. */ for (;;) { - spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); - skb = skb_peek(&sta->ps_tx_buf); + spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); + skb = skb_peek(&sta->ps_tx_buf[ac]); if (sta_info_buffer_expired(sta, skb)) - skb = __skb_dequeue(&sta->ps_tx_buf); + skb = __skb_dequeue(&sta->ps_tx_buf[ac]); else skb = NULL; - spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); + spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); /* * frames are queued in order, so if this one @@ -779,8 +819,25 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, * used to check whether the cleanup timer still needs to run, * if there are no frames we don't need to rearm the timer. */ - return !(skb_queue_empty(&sta->ps_tx_buf) && - skb_queue_empty(&sta->tx_filtered)); + return !(skb_queue_empty(&sta->ps_tx_buf[ac]) && + skb_queue_empty(&sta->tx_filtered[ac])); +} + +static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, + struct sta_info *sta) +{ + bool have_buffered = false; + int ac; + + /* This is only necessary for stations on BSS interfaces */ + if (!sta->sdata->bss) + return false; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + have_buffered |= + sta_info_cleanup_expire_buffered_ac(local, sta, ac); + + return have_buffered; } static int __must_check __sta_info_destroy(struct sta_info *sta) @@ -788,7 +845,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; unsigned long flags; - int ret, i; + int ret, i, ac; might_sleep(); @@ -856,9 +913,11 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) */ synchronize_rcu(); - local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf); - __skb_queue_purge(&sta->ps_tx_buf); - __skb_queue_purge(&sta->tx_filtered); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->ps_tx_buf[ac]); + __skb_queue_purge(&sta->tx_filtered[ac]); + } #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -1055,17 +1114,33 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - int sent, buffered; + struct sk_buff_head pending; + int filtered = 0, buffered = 0, ac; + + BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); + sta->driver_buffered_tids = 0; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); + skb_queue_head_init(&pending); + /* Send all buffered frames to the station */ - sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); - buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, - clear_sta_ps_flags, sta); - sent += buffered; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + int count = skb_queue_len(&pending), tmp; + + skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); + tmp = skb_queue_len(&pending); + filtered += tmp - count; + count = tmp; + + skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); + tmp = skb_queue_len(&pending); + buffered += tmp - count; + } + + ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); + local->total_ps_buffered -= buffered; sta_info_recalc_tim(sta); @@ -1073,7 +1148,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " "since STA not sleeping anymore\n", sdata->name, - sta->sta.addr, sta->sta.aid, sent - buffered, buffered); + sta->sta.addr, sta->sta.aid, filtered, buffered); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } @@ -1081,17 +1156,43 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - struct sk_buff *skb; - int no_pending_pkts; + struct sk_buff *skb = NULL; + bool more_data = false; + int ac; + u8 ignore_for_response = sta->sta.uapsd_queues; - skb = skb_dequeue(&sta->tx_filtered); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) - local->total_ps_buffered--; + /* + * If all ACs are delivery-enabled then we should reply + * from any of them, if only some are enabled we reply + * only from the non-enabled ones. + */ + if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_response = 0; + + /* + * Get response frame and more data bit for it. + */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + if (ignore_for_response & BIT(ac)) + continue; + + if (!skb) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + } + + /* FIXME: take into account driver-buffered frames */ + + if (!skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac])) { + more_data = true; + break; + } } - no_pending_pkts = skb_queue_empty(&sta->tx_filtered) && - skb_queue_empty(&sta->ps_tx_buf); if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1105,14 +1206,13 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); + printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n", + sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ /* Use MoreData flag to indicate whether there are more * buffered frames for this STA */ - if (no_pending_pkts) + if (!more_data) hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); else hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); @@ -1154,10 +1254,14 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - if (!buffered) + if (WARN_ON(tid >= STA_TID_NUM)) return; - set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF); + if (buffered) + set_bit(tid, &sta->driver_buffered_tids); + else + clear_bit(tid, &sta->driver_buffered_tids); + sta_info_recalc_tim(sta); } EXPORT_SYMBOL(ieee80211_sta_set_buffered); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c9ffb7ce636b..8589afad3295 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -43,8 +43,6 @@ * be in the queues * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping * station in power-save mode, reply when the driver unblocks. - * @WLAN_STA_PS_DRIVER_BUF: Station has frames pending in driver internal - * buffers. Automatically cleared on station wake-up. * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. @@ -63,7 +61,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_BLOCK_BA = 1<<11, WLAN_STA_PS_DRIVER = 1<<12, WLAN_STA_PSPOLL = 1<<13, - WLAN_STA_PS_DRIVER_BUF = 1<<14, WLAN_STA_TDLS_PEER = 1<<15, WLAN_STA_TDLS_PEER_AUTH = 1<<16, }; @@ -212,11 +209,13 @@ struct sta_ampdu_mlme { * @drv_unblock_wk: used for driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP * @flags: STA flags, see &enum ieee80211_sta_info_flags - * @ps_tx_buf: buffer of frames to transmit to this station - * when it leaves power saving state - * @tx_filtered: buffer of frames we already tried to transmit - * but were filtered by hardware due to STA having entered - * power saving state + * @ps_tx_buf: buffers (per AC) of frames to transmit to this station + * when it leaves power saving state or polls + * @tx_filtered: buffers (per AC) of frames we already tried to + * transmit but were filtered by hardware due to STA having + * entered power saving state, these are also delivered to + * the station when it leaves powersave or polls for frames + * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station @@ -286,8 +285,9 @@ struct sta_info { * STA powersave frame queues, no more than the internal * locking required. */ - struct sk_buff_head ps_tx_buf; - struct sk_buff_head tx_filtered; + struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; + struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; + unsigned long driver_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets, rx_bytes; @@ -434,8 +434,8 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) #define STA_HASH(sta) (sta[5]) -/* Maximum number of frames to buffer per power saving station */ -#define STA_MAX_TX_BUFFER 128 +/* Maximum number of frames to buffer per power saving station per AC */ +#define STA_MAX_TX_BUFFER 64 /* Minimum buffered frame expiry time. If STA uses listen interval that is * smaller than this value, the minimum value here is used instead. */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 783542a8ea20..c06857bbd573 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -14,6 +14,7 @@ #include "rate.h" #include "mesh.h" #include "led.h" +#include "wme.h" void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, @@ -43,6 +44,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + int ac; /* * This skb 'survived' a round-trip through the driver, and @@ -62,6 +65,14 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, sta->tx_filtered_count++; + if (ieee80211_is_data_qos(hdr->frame_control)) { + int tid = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + } else { + ac = IEEE80211_AC_BE; + } + /* * Clear the TX filter mask for this STA when sending the next * packet. If the STA went to power save mode, this will happen @@ -104,8 +115,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * unknown. */ if (test_sta_flags(sta, WLAN_STA_PS_STA) && - skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { - skb_queue_tail(&sta->tx_filtered, skb); + skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { + skb_queue_tail(&sta->tx_filtered[ac], skb); sta_info_recalc_tim(sta); if (!timer_pending(&local->sta_cleanup)) @@ -127,7 +138,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, if (net_ratelimit()) wiphy_debug(local->hw.wiphy, "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", - skb_queue_len(&sta->tx_filtered), + skb_queue_len(&sta->tx_filtered[ac]), !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); #endif dev_kfree_skb(skb); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d6754908ff79..a1029449df44 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -343,13 +343,22 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) total += skb_queue_len(&ap->ps_bc_buf); } + /* + * Drop one frame from each station from the lowest-priority + * AC that has frames at all. + */ list_for_each_entry_rcu(sta, &local->sta_list, list) { - skb = skb_dequeue(&sta->ps_tx_buf); - if (skb) { - purged++; - dev_kfree_skb(skb); + int ac; + + for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + total += skb_queue_len(&sta->ps_tx_buf[ac]); + if (skb) { + purged++; + dev_kfree_skb(skb); + break; + } } - total += skb_queue_len(&sta->ps_tx_buf); } rcu_read_unlock(); @@ -448,22 +457,21 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { + int ac = skb_get_queue_mapping(tx->skb); + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " - "before %d)\n", - sta->sta.addr, sta->sta.aid, - skb_queue_len(&sta->ps_tx_buf)); + printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n", + sta->sta.addr, sta->sta.aid, ac); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); - if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) { - struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf); + if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { + struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: STA %pM TX " - "buffer full - dropping oldest frame\n", - tx->sdata->name, sta->sta.addr); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: STA %pM TX buffer for " + "AC %d full - dropping oldest frame\n", + tx->sdata->name, sta->sta.addr, ac); #endif dev_kfree_skb(old); } else @@ -472,7 +480,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) info->control.jiffies = jiffies; info->control.vif = &tx->sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - skb_queue_tail(&sta->ps_tx_buf, tx->skb); + skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); if (!timer_pending(&local->sta_cleanup)) mod_timer(&local->sta_cleanup, From b0b97a8ad5c4640785f9a1c8e979f1c0fba147e1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:30 +0200 Subject: [PATCH 129/155] mac80211: remove return value from add_pending_skbs Now that we no longer use the return value, we no longer need to maintain it either, so remove it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 10 +++++----- net/mac80211/util.c | 17 +++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5cadcbbc9a57..674b23ea14d7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1303,11 +1303,11 @@ void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason); void ieee80211_add_pending_skb(struct ieee80211_local *local, struct sk_buff *skb); -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs); -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, - struct sk_buff_head *skbs, - void (*fn)(void *data), void *data); +void ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs); +void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 9d4f14621bb0..60dc600ab65b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -367,14 +367,14 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, - struct sk_buff_head *skbs, - void (*fn)(void *data), void *data) +void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data) { struct ieee80211_hw *hw = &local->hw; struct sk_buff *skb; unsigned long flags; - int queue, ret = 0, i; + int queue, i; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for (i = 0; i < hw->queues; i++) @@ -389,7 +389,6 @@ int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, continue; } - ret++; queue = skb_get_queue_mapping(skb); __skb_queue_tail(&local->pending[queue], skb); } @@ -401,14 +400,12 @@ int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - - return ret; } -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs) +void ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs) { - return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); + ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); } void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, From 8a8656fa5bbbc8568348d95184d374edb03a48b7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:31 +0200 Subject: [PATCH 130/155] mac80211: clear more-data bit on filtered frames It doesn't seem likely, but maybe possible, that the more-data bit needs to be recomputed due to changes in the queued frames. Clear it for filtered frames to ensure that we never send it incorrectly. It'll be set again as necessary when we retransmit this frame. The more likely case is maybe where the station woke up after the filtered frame in which case more-data should be clear when the frame is transmitted to the station since it is now awake. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/status.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index c06857bbd573..94475eb51d28 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -65,6 +65,16 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, sta->tx_filtered_count++; + /* + * Clear more-data bit on filtered frames, it might be set + * but later frames might time out so it might have to be + * clear again ... It's all rather unlikely (this frame + * should time out first, right?) but let's not confuse + * peers unnecessarily. + */ + if (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); + if (ieee80211_is_data_qos(hdr->frame_control)) { int tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; From 4049e09acdf4ffd270cb8fbf1cf5b39c3d02357c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:32 +0200 Subject: [PATCH 131/155] mac80211: allow releasing driver-buffered frames If there are frames for a station buffered in the driver, mac80211 announces those in the TIM IE but there's no way to release them. Add new API to release such frames and use it when the station polls for a frame. Since the API will soon also be used for uAPSD it is easily extensible. Note that before this change drivers announcing driver-buffered frames in the TIM bit actually will respond to a PS-Poll with a potentially lower priority frame (if there are any frames buffered in mac80211), after this patch a driver that hasn't been changed will no longer respond at all. This only affects ath9k, which will need to be fixed to implement the new API. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 31 ++++++++++++++ net/mac80211/driver-ops.h | 15 +++++++ net/mac80211/driver-trace.h | 35 ++++++++++++++++ net/mac80211/sta_info.c | 82 +++++++++++++++++++++++++++++-------- 4 files changed, 147 insertions(+), 16 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index acf9eaf59641..dc2ea46eb5d6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1621,6 +1621,14 @@ enum ieee80211_tx_sync_type { IEEE80211_TX_SYNC_ACTION, }; +/** + * enum ieee80211_frame_release_type - frame release reason + * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + */ +enum ieee80211_frame_release_type { + IEEE80211_FRAME_RELEASE_PSPOLL, +}; + /** * struct ieee80211_ops - callbacks from mac80211 to the driver * @@ -1931,6 +1939,23 @@ enum ieee80211_tx_sync_type { * The callback can sleep. * @rssi_callback: Notify driver when the average RSSI goes above/below * thresholds that were registered previously. The callback can sleep. + * + * @release_buffered_frames: Release buffered frames according to the given + * parameters. In the case where the driver buffers some frames for + * sleeping stations mac80211 will use this callback to tell the driver + * to release some frames, either for PS-poll or uAPSD. + * Note that if the @more_data paramter is %false the driver must check + * if there are more frames on the given TIDs, and if there are more than + * the frames being released then it must still set the more-data bit in + * the frame. If the @more_data parameter is %true, then of course the + * more-data bit must always be set. + * The @tids parameter tells the driver which TIDs to release frames + * from, for PS-poll it will always have only a single bit set. + * In the case this is used for uAPSD, the @num_frames parameter may be + * bigger than one, but the driver may send fewer frames (it must send + * at least one, however). In this case it is also responsible for + * setting the EOSP flag in the QoS header of the frames. + * This callback must be atomic. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2045,6 +2070,12 @@ struct ieee80211_ops { const struct cfg80211_bitrate_mask *mask); void (*rssi_callback)(struct ieee80211_hw *hw, enum ieee80211_rssi_event rssi_event); + + void (*release_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4f845c0845ee..8fa0d2edf54c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -670,4 +670,19 @@ static inline void drv_rssi_callback(struct ieee80211_local *local, local->ops->rssi_callback(&local->hw, event); trace_drv_return_void(local); } + +static inline void +drv_release_buffered_frames(struct ieee80211_local *local, + struct sta_info *sta, u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + trace_drv_release_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->release_buffered_frames) + local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, + num_frames, reason, + more_data); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index a46b279bbbe4..531fbd086794 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1129,6 +1129,41 @@ TRACE_EVENT(drv_rssi_callback, ) ); +TRACE_EVENT(drv_release_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(u16, tids) + __field(int, num_frames) + __field(int, reason) + __field(bool, more_data) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->tids = tids; + __entry->num_frames = num_frames; + __entry->reason = reason; + __entry->more_data = more_data; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT + " TIDs:0x%.4x frames:%d reason:%d more:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, + __entry->reason, __entry->more_data + ) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4d85672f0b8f..b3f841948c09 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1157,8 +1157,10 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff *skb = NULL; + bool found = false; bool more_data = false; int ac; + unsigned long driver_release_tids = 0; u8 ignore_for_response = sta->sta.uapsd_queues; /* @@ -1173,20 +1175,41 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * Get response frame and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + unsigned long tids; + if (ignore_for_response & BIT(ac)) continue; - if (!skb) { - skb = skb_dequeue(&sta->tx_filtered[ac]); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf[ac]); + tids = ieee80211_tids_for_ac(ac); + + if (!found) { + driver_release_tids = sta->driver_buffered_tids & tids; + if (driver_release_tids) { + found = true; + } else { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue(&sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } if (skb) - local->total_ps_buffered--; + found = true; + } + + /* + * If the driver has data on more than one TID then + * certainly there's more data if we release just a + * single frame now (from a single TID). + */ + if (hweight16(driver_release_tids) > 1) { + more_data = true; + driver_release_tids = + BIT(ffs(driver_release_tids) - 1); + break; } } - /* FIXME: take into account driver-buffered frames */ - if (!skb_queue_empty(&sta->tx_filtered[ac]) || !skb_queue_empty(&sta->ps_tx_buf[ac])) { more_data = true; @@ -1194,6 +1217,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) } } + if (!found) { +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + /* + * FIXME: This can be the result of a race condition between + * us expiring a frame and the station polling for it. + * Should we send it a null-func frame indicating we + * have nothing buffered for it? + */ + printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " + "though there are no buffered frames for it\n", + sdata->name, sta->sta.addr); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + + return; + } + if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = @@ -1220,18 +1259,29 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) ieee80211_add_pending_skb(local, skb); sta_info_recalc_tim(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG } else { /* - * FIXME: This can be the result of a race condition between - * us expiring a frame and the station polling for it. - * Should we send it a null-func frame indicating we - * have nothing buffered for it? + * We need to release a frame that is buffered somewhere in the + * driver ... it'll have to handle that. + * Note that, as per the comment above, it'll also have to see + * if there is more than just one frame on the specific TID that + * we're releasing from, and it needs to set the more-data bit + * accordingly if we tell it that there's no more data. If we do + * tell it there's more data, then of course the more-data bit + * needs to be set anyway. + */ + drv_release_buffered_frames(local, sta, driver_release_tids, + 1, IEEE80211_FRAME_RELEASE_PSPOLL, + more_data); + + /* + * Note that we don't recalculate the TIM bit here as it would + * most likely have no effect at all unless the driver told us + * that the TID became empty before returning here from the + * release function. + * Either way, however, when the driver tells us that the TID + * became empty we'll do the TIM recalculation. */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } } From 47086fc51aa2220f58049704a8b73e4fcdf372b9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:33 +0200 Subject: [PATCH 132/155] mac80211: implement uAPSD Add uAPSD support to mac80211. This is probably not possible with all devices, so advertising it with the cfg80211 flag will be left up to drivers that want it. Due to my previous patches it is now a fairly straight-forward extension. Drivers need to have accurate TX status reporting for the EOSP frame. For drivers that buffer themselves, the provided APIs allow releasing the right number of frames, but then drivers need to set EOSP and more-data themselves. This is documented in more detail in the new code itself. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/iwl-4965-tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 2 +- drivers/net/wireless/p54/txrx.c | 2 +- include/net/mac80211.h | 24 ++- net/mac80211/rx.c | 102 ++++++++---- net/mac80211/sta_info.c | 172 ++++++++++++++------ net/mac80211/sta_info.h | 8 + net/mac80211/status.c | 15 +- net/mac80211/tx.c | 8 +- 9 files changed, 245 insertions(+), 90 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c index ac4f64de1363..7f12e3638bae 100644 --- a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c +++ b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c @@ -335,7 +335,7 @@ int iwl4965_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 0e5d6498be21..dcb3bd67d4f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -300,7 +300,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)info->control.sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 2b97a89e7ff8..f485784a60ae 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -689,7 +689,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE) + if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dc2ea46eb5d6..dd07964ef154 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -339,9 +339,9 @@ struct ieee80211_bss_conf { * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted - * @IEEE80211_TX_CTL_PSPOLL_RESPONSE: (internal?) - * This frame is a response to a PS-poll frame and should be sent - * although the station is in powersave mode. + * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll + * frame (PS-Poll or uAPSD) and should be sent although the station + * is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the @@ -367,6 +367,10 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. * This flag is actually used for management frame especially for P2P * frames not being sent at CCK rate in 2GHz band. + * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, + * when its status is reported the service period ends. For frames in + * an SP that mac80211 transmits, it is already set; for driver frames + * the driver may set this flag. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -388,7 +392,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), - IEEE80211_TX_CTL_PSPOLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_POLL_RESPONSE = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20), @@ -398,6 +402,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), + IEEE80211_TX_STATUS_EOSP = BIT(28), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 @@ -411,9 +416,9 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ - IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ - IEEE80211_TX_CTL_STBC) + IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) /** * enum mac80211_rate_control_flags - per-rate flags set by the @@ -1624,9 +1629,12 @@ enum ieee80211_tx_sync_type { /** * enum ieee80211_frame_release_type - frame release reason * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll + * @IEEE80211_FRAME_RELEASE_UAPSD: frame(s) released due to + * frame received on trigger-enabled AC */ enum ieee80211_frame_release_type { IEEE80211_FRAME_RELEASE_PSPOLL, + IEEE80211_FRAME_RELEASE_UAPSD, }; /** @@ -1954,7 +1962,9 @@ enum ieee80211_frame_release_type { * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for - * setting the EOSP flag in the QoS header of the frames. + * setting the EOSP flag in the QoS header of the frames. Also, when the + * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP + * on the last frame in the SP. * This callback must be atomic. */ struct ieee80211_ops { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index db46601e50bf..9a703f00b5fb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1162,6 +1162,79 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) } EXPORT_SYMBOL(ieee80211_sta_ps_transition); +static ieee80211_rx_result debug_noinline +ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) +{ + struct ieee80211_sub_if_data *sdata = rx->sdata; + struct ieee80211_hdr *hdr = (void *)rx->skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int tid, ac; + + if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) + return RX_CONTINUE; + + if (sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return RX_CONTINUE; + + /* + * The device handles station powersave, so don't do anything about + * uAPSD and PS-Poll frames (the latter shouldn't even come up from + * it to mac80211 since they're handled.) + */ + if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return RX_CONTINUE; + + /* + * Don't do anything if the station isn't already asleep. In + * the uAPSD case, the station will probably be marked asleep, + * in the PS-Poll case the station must be confused ... + */ + if (!test_sta_flags(rx->sta, WLAN_STA_PS_STA)) + return RX_CONTINUE; + + if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + + /* Free PS Poll skb here instead of returning RX_DROP that would + * count as an dropped frame. */ + dev_kfree_skb(rx->skb); + + return RX_QUEUED; + } else if (!ieee80211_has_morefrags(hdr->frame_control) && + !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && + ieee80211_has_pm(hdr->frame_control) && + (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control))) { + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + + /* + * If this AC is not trigger-enabled do nothing. + * + * NB: This could/should check a separate bitmap of trigger- + * enabled queues, but for now we only implement uAPSD w/o + * TSPEC changes to the ACs, so they're always the same. + */ + if (!(rx->sta->sta.uapsd_queues & BIT(ac))) + return RX_CONTINUE; + + /* if we are in a service period, do nothing */ + if (test_sta_flags(rx->sta, WLAN_STA_SP)) + return RX_CONTINUE; + + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_uapsd(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_UAPSD); + } + + return RX_CONTINUE; +} + static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { @@ -1472,33 +1545,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) return RX_CONTINUE; } -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) -{ - struct ieee80211_sub_if_data *sdata = rx->sdata; - __le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - - if (likely(!rx->sta || !ieee80211_is_pspoll(fc) || - !(status->rx_flags & IEEE80211_RX_RA_MATCH))) - return RX_CONTINUE; - - if ((sdata->vif.type != NL80211_IFTYPE_AP) && - (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) - return RX_DROP_UNUSABLE; - - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); - - /* Free PS Poll skb here instead of returning RX_DROP that would - * count as an dropped frame. */ - dev_kfree_skb(rx->skb); - - return RX_QUEUED; -} - static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { @@ -2567,9 +2613,9 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_check_more_data) + CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_defragment) - CALL_RXH(ieee80211_rx_h_ps_poll) CALL_RXH(ieee80211_rx_h_michael_mic_verify) /* must be after MMIC verify so header is counted in MPDU mic */ #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b3f841948c09..f9079e478f77 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -248,6 +248,9 @@ static void sta_unblock(struct work_struct *wk) else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { clear_sta_flags(sta, WLAN_STA_PS_DRIVER); ieee80211_sta_ps_deliver_poll_response(sta); + } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { + clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + ieee80211_sta_ps_deliver_uapsd(sta); } else clear_sta_flags(sta, WLAN_STA_PS_DRIVER); } @@ -1117,6 +1120,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct sk_buff_head pending; int filtered = 0, buffered = 0, ac; + clear_sta_flags(sta, WLAN_STA_SP); + BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); sta->driver_buffered_tids = 0; @@ -1152,32 +1157,28 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } -void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +static void +ieee80211_sta_ps_deliver_response(struct sta_info *sta, + int n_frames, u8 ignored_acs, + enum ieee80211_frame_release_type reason) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = NULL; bool found = false; bool more_data = false; int ac; unsigned long driver_release_tids = 0; - u8 ignore_for_response = sta->sta.uapsd_queues; + struct sk_buff_head frames; + + __skb_queue_head_init(&frames); /* - * If all ACs are delivery-enabled then we should reply - * from any of them, if only some are enabled we reply - * only from the non-enabled ones. - */ - if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) - ignore_for_response = 0; - - /* - * Get response frame and more data bit for it. + * Get response frame(s) and more data bit for it. */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { unsigned long tids; - if (ignore_for_response & BIT(ac)) + if (ignored_acs & BIT(ac)) continue; tids = ieee80211_tids_for_ac(ac); @@ -1187,14 +1188,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) if (driver_release_tids) { found = true; } else { - skb = skb_dequeue(&sta->tx_filtered[ac]); - if (!skb) { - skb = skb_dequeue(&sta->ps_tx_buf[ac]); - if (skb) - local->total_ps_buffered--; - } - if (skb) + struct sk_buff *skb; + + while (n_frames > 0) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue( + &sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + if (!skb) + break; + n_frames--; found = true; + __skb_queue_tail(&frames, skb); + } } /* @@ -1202,7 +1211,8 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * certainly there's more data if we release just a * single frame now (from a single TID). */ - if (hweight16(driver_release_tids) > 1) { + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && + hweight16(driver_release_tids) > 1) { more_data = true; driver_release_tids = BIT(ffs(driver_release_tids) - 1); @@ -1225,38 +1235,56 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * Should we send it a null-func frame indicating we * have nothing buffered for it? */ - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) + printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " + "though there are no buffered frames for it\n", + sdata->name, sta->sta.addr); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ return; } - if (skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) skb->data; + if (!driver_release_tids) { + struct sk_buff_head pending; + struct sk_buff *skb; - /* - * Tell TX path to send this frame even though the STA may - * still remain is PS mode after this frame exchange. - */ - info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; + skb_queue_head_init(&pending); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n", - sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + while ((skb = __skb_dequeue(&frames))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *) skb->data; - /* Use MoreData flag to indicate whether there are more - * buffered frames for this STA */ - if (!more_data) - hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); - else - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; - ieee80211_add_pending_skb(local, skb); + /* + * Use MoreData flag to indicate whether there are + * more buffered frames for this STA + */ + if (!more_data) + hdr->frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (reason == IEEE80211_FRAME_RELEASE_UAPSD && + skb_queue_empty(&frames)) { + /* set EOSP for the frame */ + u8 *p = ieee80211_get_qos_ctl(hdr); + *p |= IEEE80211_QOS_CTL_EOSP; + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + } + + __skb_queue_tail(&pending, skb); + } + + ieee80211_add_pending_skbs(local, &pending); sta_info_recalc_tim(sta); } else { @@ -1271,8 +1299,7 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) * needs to be set anyway. */ drv_release_buffered_frames(local, sta, driver_release_tids, - 1, IEEE80211_FRAME_RELEASE_PSPOLL, - more_data); + n_frames, reason, more_data); /* * Note that we don't recalculate the TIM bit here as it would @@ -1285,6 +1312,59 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) } } +void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +{ + u8 ignore_for_response = sta->sta.uapsd_queues; + + /* + * If all ACs are delivery-enabled then we should reply + * from any of them, if only some are enabled we reply + * only from the non-enabled ones. + */ + if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) + ignore_for_response = 0; + + ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response, + IEEE80211_FRAME_RELEASE_PSPOLL); +} + +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) +{ + int n_frames = sta->sta.max_sp; + u8 delivery_enabled = sta->sta.uapsd_queues; + + /* + * If we ever grow support for TSPEC this might happen if + * the TSPEC update from hostapd comes in between a trigger + * frame setting WLAN_STA_UAPSD in the RX path and this + * actually getting called. + */ + if (!delivery_enabled) + return; + + /* Ohh, finally, the service period starts :-) */ + set_sta_flags(sta, WLAN_STA_SP); + + switch (sta->sta.max_sp) { + case 1: + n_frames = 2; + break; + case 2: + n_frames = 4; + break; + case 3: + n_frames = 6; + break; + case 0: + /* XXX: what is a good value? */ + n_frames = 8; + break; + } + + ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled, + IEEE80211_FRAME_RELEASE_UAPSD); +} + void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8589afad3295..751ad25f925f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -46,6 +46,11 @@ * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. + * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was + * keeping station in power-save mode, reply when the driver + * unblocks the station. + * @WLAN_STA_SP: Station is in a service period, so don't try to + * reply to other uAPSD trigger frames. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -63,6 +68,8 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL = 1<<13, WLAN_STA_TDLS_PEER = 1<<15, WLAN_STA_TDLS_PEER_AUTH = 1<<16, + WLAN_STA_UAPSD = 1<<17, + WLAN_STA_SP = 1<<18, }; #define STA_TID_NUM 16 @@ -539,5 +546,6 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 94475eb51d28..b5df9be4d043 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -76,8 +76,16 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); if (ieee80211_is_data_qos(hdr->frame_control)) { - int tid = *ieee80211_get_qos_ctl(hdr) & - IEEE80211_QOS_CTL_TID_MASK; + u8 *p = ieee80211_get_qos_ctl(hdr); + int tid = *p & IEEE80211_QOS_CTL_TID_MASK; + + /* + * Clear EOSP if set, this could happen e.g. + * if an absence period (us being a P2P GO) + * shortens the SP. + */ + if (*p & IEEE80211_QOS_CTL_EOSP) + *p &= ~IEEE80211_QOS_CTL_EOSP; ac = ieee802_1d_to_ac[tid & 7]; } else { ac = IEEE80211_AC_BE; @@ -276,6 +284,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN)) continue; + if (info->flags & IEEE80211_TX_STATUS_EOSP) + clear_sta_flags(sta, WLAN_STA_SP); + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) { /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a1029449df44..a0676d39fe8f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -456,7 +456,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) staflags = get_sta_flags(sta); if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && - !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { + !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { int ac = skb_get_queue_mapping(tx->skb); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG @@ -497,9 +497,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) } #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG else if (unlikely(staflags & WLAN_STA_PS_STA)) { - printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll " - "set -> send frame\n", tx->sdata->name, - sta->sta.addr); + printk(KERN_DEBUG + "%s: STA %pM in PS mode, but polling/in SP -> send frame\n", + tx->sdata->name, sta->sta.addr); } #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ From ce662b44ce22e3e8886104d5feb2a451d7ba560f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:34 +0200 Subject: [PATCH 133/155] mac80211: send (QoS) Null if no buffered frames For PS-poll, there's a possible race between us expiring a frame and the station polling for it -- send it a null frame in that case. For uAPSD, the standard says that we have to send a frame in each SP, so send null if we don't have any other frames. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/sta_info.c | 104 ++++++++++++++++++++++++++++++++----- net/mac80211/tx.c | 3 +- 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 674b23ea14d7..da3206450192 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1272,6 +1272,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke struct ieee80211_hdr *hdr, const u8 *tsc, gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f9079e478f77..d9cb56f548a9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -24,6 +24,7 @@ #include "sta_info.h" #include "debugfs_sta.h" #include "mesh.h" +#include "wme.h" /** * DOC: STA information lifetime rules @@ -247,10 +248,16 @@ static void sta_unblock(struct work_struct *wk) ieee80211_sta_ps_deliver_wakeup(sta); else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); ieee80211_sta_ps_deliver_poll_response(sta); + local_bh_enable(); } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + + local_bh_disable(); ieee80211_sta_ps_deliver_uapsd(sta); + local_bh_enable(); } else clear_sta_flags(sta, WLAN_STA_PS_DRIVER); } @@ -1157,6 +1164,70 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ } +static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, int tid, + bool uapsd) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_qos_hdr *nullfunc; + struct sk_buff *skb; + int size = sizeof(*nullfunc); + __le16 fc; + bool qos = test_sta_flags(sta, WLAN_STA_WME); + struct ieee80211_tx_info *info; + + if (qos) { + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } else { + size -= 2; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (void *) skb_put(skb, size); + nullfunc->frame_control = fc; + nullfunc->duration_id = 0; + memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); + + if (qos) { + skb->priority = tid; + + skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); + + nullfunc->qos_ctrl = cpu_to_le16(tid); + + if (uapsd) + nullfunc->qos_ctrl |= + cpu_to_le16(IEEE80211_QOS_CTL_EOSP); + } + + info = IEEE80211_SKB_CB(skb); + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. + */ + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; + + if (uapsd) + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + ieee80211_xmit(sdata, skb); +} + static void ieee80211_sta_ps_deliver_response(struct sta_info *sta, int n_frames, u8 ignored_acs, @@ -1228,19 +1299,28 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, } if (!found) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - /* - * FIXME: This can be the result of a race condition between - * us expiring a frame and the station polling for it. - * Should we send it a null-func frame indicating we - * have nothing buffered for it? - */ - if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) - printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " - "though there are no buffered frames for it\n", - sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + int tid; + /* + * For PS-Poll, this can only happen due to a race condition + * when we set the TIM bit and the station notices it, but + * before it can poll for the frame we expire it. + * + * For uAPSD, this is said in the standard (11.2.1.5 h): + * At each unscheduled SP for a non-AP STA, the AP shall + * attempt to transmit at least one MSDU or MMPDU, but no + * more than the value specified in the Max SP Length field + * in the QoS Capability element from delivery-enabled ACs, + * that are destined for the non-AP STA. + * + * Since we have no other MSDU/MMPDU, transmit a QoS null frame. + */ + + /* This will evaluate to 1, 3, 5 or 7. */ + tid = 7 - ((ffs(~ignored_acs) - 1) << 1); + + ieee80211_send_null_response(sdata, sta, tid, + reason == IEEE80211_FRAME_RELEASE_UAPSD); return; } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a0676d39fe8f..5bf91c43c88c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1520,8 +1520,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, return 0; } -static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); From deeaee197b0fa694ba6c8f02cdb57b3be7115b4f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:35 +0200 Subject: [PATCH 134/155] mac80211: reply only once to each PS-poll If a PS-poll frame is retried (but was received) there is no way to detect that since it has no sequence number. As a consequence, the standard asks us to not react to PS-poll frames until the response to one made it out (was ACKed or lost). Implement this by using the WLAN_STA_SP flags to also indicate a PS-Poll "service period" and the IEEE80211_TX_STATUS_EOSP flag for the response packet to indicate the end of the "SP" as usual. We could use separate flags, but that will most likely completely confuse drivers, and while the standard doesn't exclude simultaneously polling using uAPSD and PS-Poll, doing that seems quite problematic. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 9 ++++++++- net/mac80211/rx.c | 10 ++++++---- net/mac80211/sta_info.c | 22 +++++++++++----------- net/mac80211/sta_info.h | 2 +- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dd07964ef154..ee6449eff7dc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -370,7 +370,8 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_STATUS_EOSP: This packet marks the end of service period, * when its status is reported the service period ends. For frames in * an SP that mac80211 transmits, it is already set; for driver frames - * the driver may set this flag. + * the driver may set this flag. It is also used to do the same for + * PS-Poll responses. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -1959,6 +1960,12 @@ enum ieee80211_frame_release_type { * more-data bit must always be set. * The @tids parameter tells the driver which TIDs to release frames * from, for PS-poll it will always have only a single bit set. + * In the case this is used for a PS-poll initiated release, the + * @num_frames parameter will always be 1 so code can be shared. In + * this case the driver must also set %IEEE80211_TX_STATUS_EOSP flag + * on the TX status (and must report TX status) so that the PS-poll + * period is properly ended. This is used to avoid sending multiple + * responses for a retried PS-poll frame. * In the case this is used for uAPSD, the @num_frames parameter may be * bigger than one, but the driver may send fewer frames (it must send * at least one, however). In this case it is also responsible for diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9a703f00b5fb..32c8ee43f720 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1194,10 +1194,12 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + if (!test_sta_flags(rx->sta, WLAN_STA_SP)) { + if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + } /* Free PS Poll skb here instead of returning RX_DROP that would * count as an dropped frame. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d9cb56f548a9..5732e4d0cc21 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1217,13 +1217,12 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, /* * Tell TX path to send this frame even though the * STA may still remain is PS mode after this frame - * exchange. + * exchange. Also set EOSP to indicate this packet + * ends the poll/service period. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; - - if (uapsd) - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; + info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; ieee80211_xmit(sdata, skb); } @@ -1241,6 +1240,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, unsigned long driver_release_tids = 0; struct sk_buff_head frames; + /* Service or PS-Poll period starts */ + set_sta_flags(sta, WLAN_STA_SP); + __skb_queue_head_init(&frames); /* @@ -1357,10 +1359,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* set EOSP for the frame */ u8 *p = ieee80211_get_qos_ctl(hdr); *p |= IEEE80211_QOS_CTL_EOSP; - info->flags |= IEEE80211_TX_STATUS_EOSP | - IEEE80211_TX_CTL_REQ_TX_STATUS; } + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + __skb_queue_tail(&pending, skb); } @@ -1422,9 +1425,6 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) if (!delivery_enabled) return; - /* Ohh, finally, the service period starts :-) */ - set_sta_flags(sta, WLAN_STA_SP); - switch (sta->sta.max_sp) { case 1: n_frames = 2; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 751ad25f925f..348847a32630 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -50,7 +50,7 @@ * keeping station in power-save mode, reply when the driver * unblocks the station. * @WLAN_STA_SP: Station is in a service period, so don't try to - * reply to other uAPSD trigger frames. + * reply to other uAPSD trigger frames or PS-Poll. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, From c2c98fdeb5c897499644eb247285c8e3dacc6450 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:36 +0200 Subject: [PATCH 135/155] mac80211: optimise station flags The flaglock in struct sta_info has long been something that I wanted to get rid of, this finally does the conversion to atomic bitops. The conversion itself is straight-forward in most places, a few things needed to change a bit since we can no longer use multiple bits at the same time. On x86-64, this is a fairly significant code size reduction: text data bss dec hex 427861 23648 1008 452517 6e7a5 before 425383 23648 976 450007 6ddd7 after Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/agg-rx.c | 2 +- net/mac80211/agg-tx.c | 2 +- net/mac80211/cfg.c | 44 +++++++++------- net/mac80211/debugfs_sta.c | 20 +++---- net/mac80211/ht.c | 2 +- net/mac80211/ibss.c | 4 +- net/mac80211/iface.c | 4 +- net/mac80211/key.c | 4 +- net/mac80211/mesh_plink.c | 8 +-- net/mac80211/mlme.c | 22 ++++---- net/mac80211/pm.c | 2 +- net/mac80211/rx.c | 28 +++++----- net/mac80211/sta_info.c | 35 +++++++------ net/mac80211/sta_info.h | 104 ++++++++++++------------------------- net/mac80211/status.c | 12 ++--- net/mac80211/tx.c | 68 +++++++++++++----------- net/mac80211/util.c | 2 +- net/mac80211/wme.c | 4 +- 18 files changed, 173 insertions(+), 194 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index e6cab51dceb0..0cde8df6828d 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -223,7 +223,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Suspend in progress. " "Denying ADDBA request\n"); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 3cef5a7281cb..2ac033989e01 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -382,7 +382,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, sdata->vif.type != NL80211_IFTYPE_AP) return -EINVAL; - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA sessions blocked. " "Denying BA session request\n"); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index bdf9852eec5b..1309bb9c97be 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -668,7 +668,6 @@ static void sta_apply_parameters(struct ieee80211_local *local, struct sta_info *sta, struct station_parameters *params) { - unsigned long flags; u32 rates; int i, j; struct ieee80211_supported_band *sband; @@ -677,49 +676,53 @@ static void sta_apply_parameters(struct ieee80211_local *local, sband = local->hw.wiphy->bands[local->oper_channel->band]; - spin_lock_irqsave(&sta->flaglock, flags); mask = params->sta_flags_mask; set = params->sta_flags_set; if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { - sta->flags &= ~WLAN_STA_AUTHORIZED; if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - sta->flags |= WLAN_STA_AUTHORIZED; + set_sta_flag(sta, WLAN_STA_AUTHORIZED); + else + clear_sta_flag(sta, WLAN_STA_AUTHORIZED); } if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { - sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) - sta->flags |= WLAN_STA_SHORT_PREAMBLE; + set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); + else + clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } if (mask & BIT(NL80211_STA_FLAG_WME)) { - sta->flags &= ~WLAN_STA_WME; - sta->sta.wme = false; if (set & BIT(NL80211_STA_FLAG_WME)) { - sta->flags |= WLAN_STA_WME; + set_sta_flag(sta, WLAN_STA_WME); sta->sta.wme = true; + } else { + clear_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = false; } } if (mask & BIT(NL80211_STA_FLAG_MFP)) { - sta->flags &= ~WLAN_STA_MFP; if (set & BIT(NL80211_STA_FLAG_MFP)) - sta->flags |= WLAN_STA_MFP; + set_sta_flag(sta, WLAN_STA_MFP); + else + clear_sta_flag(sta, WLAN_STA_MFP); } if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - sta->flags &= ~WLAN_STA_AUTH; if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) - sta->flags |= WLAN_STA_AUTH; + set_sta_flag(sta, WLAN_STA_AUTH); + else + clear_sta_flag(sta, WLAN_STA_AUTH); } if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { - sta->flags &= ~WLAN_STA_TDLS_PEER; if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) - sta->flags |= WLAN_STA_TDLS_PEER; + set_sta_flag(sta, WLAN_STA_TDLS_PEER); + else + clear_sta_flag(sta, WLAN_STA_TDLS_PEER); } - spin_unlock_irqrestore(&sta->flaglock, flags); if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { sta->sta.uapsd_queues = params->uapsd_queues; @@ -815,12 +818,13 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!sta) return -ENOMEM; - sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_ASSOC); sta_apply_parameters(local, sta, params); /* Only TDLS-supporting stations can add TDLS peers */ - if ((sta->flags & WLAN_STA_TDLS_PEER) && + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && sdata->vif.type == NL80211_IFTYPE_STATION)) return -ENOTSUPP; @@ -880,7 +884,7 @@ static int ieee80211_change_station(struct wiphy *wiphy, /* The TDLS bit cannot be toggled after the STA was added */ if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != - !!test_sta_flags(sta, WLAN_STA_TDLS_PEER)) { + !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { rcu_read_unlock(); return -EINVAL; } @@ -2449,7 +2453,7 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, return -ENOLINK; } - set_sta_flags(sta, WLAN_STA_TDLS_PEER_AUTH); + set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); rcu_read_unlock(); break; case NL80211_TDLS_DISABLE_LINK: diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 20ec2b0cb3c1..56bb68b9c42d 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -58,17 +58,17 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, { char buf[100]; struct sta_info *sta = file->private_data; - u32 staflags = get_sta_flags(sta); + int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", - staflags & WLAN_STA_AUTH ? "AUTH\n" : "", - staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "", - staflags & WLAN_STA_PS_STA ? "PS (sta)\n" : "", - staflags & WLAN_STA_PS_DRIVER ? "PS (driver)\n" : "", - staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", - staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", - staflags & WLAN_STA_WME ? "WME\n" : "", - staflags & WLAN_STA_WDS ? "WDS\n" : "", - staflags & WLAN_STA_MFP ? "MFP\n" : ""); + test_sta_flag(sta, WLAN_STA_AUTH) ? "AUTH\n" : "", + test_sta_flag(sta, WLAN_STA_ASSOC) ? "ASSOC\n" : "", + test_sta_flag(sta, WLAN_STA_PS_STA) ? "PS (sta)\n" : "", + test_sta_flag(sta, WLAN_STA_PS_DRIVER) ? "PS (driver)\n" : "", + test_sta_flag(sta, WLAN_STA_AUTHORIZED) ? "AUTHORIZED\n" : "", + test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE) ? "SHORT PREAMBLE\n" : "", + test_sta_flag(sta, WLAN_STA_WME) ? "WME\n" : "", + test_sta_flag(sta, WLAN_STA_WDS) ? "WDS\n" : "", + test_sta_flag(sta, WLAN_STA_MFP) ? "MFP\n" : ""); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 2b9b52c69569..f80a35c0d000 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -130,7 +130,7 @@ void ieee80211_ba_session_work(struct work_struct *work) * down by the code that set the flag, so this * need not run. */ - if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) return; mutex_lock(&sta->ampdu_mlme.mtx); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 7809895df8b0..2da3040787a7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -314,7 +314,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } if (sta && elems->wmm_info) - set_sta_flags(sta, WLAN_STA_WME); + set_sta_flag(sta, WLAN_STA_WME); rcu_read_unlock(); } @@ -452,7 +452,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, return NULL; sta->last_rx = jiffies; - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4116a7542b6b..ef741e8dbedb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -299,8 +299,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) goto err_del_interface; } - /* no locking required since STA is not live yet */ - sta->flags |= WLAN_STA_AUTHORIZED; + /* no atomic bitop required since STA is not live yet */ + set_sta_flag(sta, WLAN_STA_AUTHORIZED); res = sta_info_insert(sta); if (res) { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 5150c6d11b57..756b157c2edd 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -464,7 +464,7 @@ int ieee80211_key_link(struct ieee80211_key *key, * some hardware cannot handle TKIP with QoS, so * we indicate whether QoS could be in use. */ - if (test_sta_flags(sta, WLAN_STA_WME)) + if (test_sta_flag(sta, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } else { if (sdata->vif.type == NL80211_IFTYPE_STATION) { @@ -478,7 +478,7 @@ int ieee80211_key_link(struct ieee80211_key *key, /* same here, the AP could be using QoS */ ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); if (ap) { - if (test_sta_flags(ap, WLAN_STA_WME)) + if (test_sta_flag(ap, WLAN_STA_WME)) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9cc5029b3c46..7e57f5d07f66 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -92,7 +92,9 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH | WLAN_STA_WME; + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_WME); sta->sta.supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta); @@ -383,7 +385,7 @@ int mesh_plink_open(struct sta_info *sta) __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; - if (!test_sta_flags(sta, WLAN_STA_AUTH)) + if (!test_sta_flag(sta, WLAN_STA_AUTH)) return -EPERM; spin_lock_bh(&sta->lock); @@ -503,7 +505,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } - if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) { + if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); return; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b98c43a7f191..c4e8901c96f6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -627,7 +627,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *mgd = &sdata->u.mgd; struct sta_info *sta = NULL; - u32 sta_flags = 0; + bool authorized = false; if (!mgd->powersave) return false; @@ -645,13 +645,10 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) rcu_read_lock(); sta = sta_info_get(sdata, mgd->bssid); if (sta) - sta_flags = get_sta_flags(sta); + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); rcu_read_unlock(); - if (!(sta_flags & WLAN_STA_AUTHORIZED)) - return false; - - return true; + return authorized; } /* need to hold RTNL or interface lock */ @@ -1095,7 +1092,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); if (sta) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, tx); } mutex_unlock(&local->sta_mtx); @@ -1513,10 +1510,11 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, return false; } - set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_ASSOC_AP); + set_sta_flag(sta, WLAN_STA_AUTH); + set_sta_flag(sta, WLAN_STA_ASSOC); + set_sta_flag(sta, WLAN_STA_ASSOC_AP); if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_AUTHORIZED); rates = 0; basic_rates = 0; @@ -1575,10 +1573,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) - set_sta_flags(sta, WLAN_STA_MFP); + set_sta_flag(sta, WLAN_STA_MFP); if (elems.wmm_param) - set_sta_flags(sta, WLAN_STA_WME); + set_sta_flag(sta, WLAN_STA_WME); /* sta_info_reinsert will also unlock the mutex lock */ err = sta_info_reinsert(sta); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 6326d3439861..9ee7164b207c 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -42,7 +42,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 32c8ee43f720..b867bd55de7a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -841,7 +841,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && - (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) { + (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { if (rx->sta && rx->sta->dummy && ieee80211_is_data_present(hdr->frame_control)) { u16 ethertype; @@ -1110,7 +1110,7 @@ static void ap_sta_ps_start(struct sta_info *sta) struct ieee80211_local *local = sdata->local; atomic_inc(&sdata->bss->num_sta_ps); - set_sta_flags(sta, WLAN_STA_PS_STA); + set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG @@ -1130,7 +1130,7 @@ static void ap_sta_ps_end(struct sta_info *sta) sdata->name, sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n", sdata->name, sta->sta.addr, sta->sta.aid); @@ -1149,7 +1149,7 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); /* Don't let the same PS state be set twice */ - in_ps = test_sta_flags(sta_inf, WLAN_STA_PS_STA); + in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); if ((start && in_ps) || (!start && !in_ps)) return -EINVAL; @@ -1190,15 +1190,15 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) * the uAPSD case, the station will probably be marked asleep, * in the PS-Poll case the station must be confused ... */ - if (!test_sta_flags(rx->sta, WLAN_STA_PS_STA)) + if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) return RX_CONTINUE; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { - if (!test_sta_flags(rx->sta, WLAN_STA_SP)) { - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ieee80211_sta_ps_deliver_poll_response(rx->sta); else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); + set_sta_flag(rx->sta, WLAN_STA_PSPOLL); } /* Free PS Poll skb here instead of returning RX_DROP that would @@ -1225,13 +1225,13 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) return RX_CONTINUE; /* if we are in a service period, do nothing */ - if (test_sta_flags(rx->sta, WLAN_STA_SP)) + if (test_sta_flag(rx->sta, WLAN_STA_SP)) return RX_CONTINUE; - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) ieee80211_sta_ps_deliver_uapsd(rx->sta); else - set_sta_flags(rx->sta, WLAN_STA_UAPSD); + set_sta_flag(rx->sta, WLAN_STA_UAPSD); } return RX_CONTINUE; @@ -1295,7 +1295,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { - if (test_sta_flags(sta, WLAN_STA_PS_STA)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * Ignore doze->wake transitions that are * indicated by non-data frames, the standard @@ -1570,7 +1570,7 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) { if (unlikely(!rx->sta || - !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED))) + !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) return -EACCES; return 0; @@ -1613,7 +1613,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DECRYPTED) return 0; - if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) { + if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) && ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && rx->key)) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 5732e4d0cc21..a00358224cd5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -244,22 +244,22 @@ static void sta_unblock(struct work_struct *wk) if (sta->dead) return; - if (!test_sta_flags(sta, WLAN_STA_PS_STA)) + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) ieee80211_sta_ps_deliver_wakeup(sta); - else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); local_bh_disable(); ieee80211_sta_ps_deliver_poll_response(sta); local_bh_enable(); - } else if (test_and_clear_sta_flags(sta, WLAN_STA_UAPSD)) { - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); local_bh_disable(); ieee80211_sta_ps_deliver_uapsd(sta); local_bh_enable(); } else - clear_sta_flags(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); } static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -292,7 +292,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; spin_lock_init(&sta->lock); - spin_lock_init(&sta->flaglock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); @@ -871,7 +870,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) * sessions -- block that to make sure the tear-down * will be sufficient. */ - set_sta_flags(sta, WLAN_STA_BLOCK_BA); + set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); spin_lock_irqsave(&local->sta_lock, flags); @@ -892,10 +891,13 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) sta->dead = true; - if (test_and_clear_sta_flags(sta, - WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { BUG_ON(!sdata->bss); + clear_sta_flag(sta, WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + atomic_dec(&sdata->bss->num_sta_ps); sta_info_recalc_tim(sta); } @@ -1116,7 +1118,8 @@ static void clear_sta_ps_flags(void *_sta) { struct sta_info *sta = _sta; - clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_STA); } /* powersave support code */ @@ -1127,7 +1130,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct sk_buff_head pending; int filtered = 0, buffered = 0, ac; - clear_sta_flags(sta, WLAN_STA_SP); + clear_sta_flag(sta, WLAN_STA_SP); BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); sta->driver_buffered_tids = 0; @@ -1173,7 +1176,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; int size = sizeof(*nullfunc); __le16 fc; - bool qos = test_sta_flags(sta, WLAN_STA_WME); + bool qos = test_sta_flag(sta, WLAN_STA_WME); struct ieee80211_tx_info *info; if (qos) { @@ -1241,7 +1244,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, struct sk_buff_head frames; /* Service or PS-Poll period starts */ - set_sta_flags(sta, WLAN_STA_SP); + set_sta_flag(sta, WLAN_STA_SP); __skb_queue_head_init(&frames); @@ -1453,8 +1456,8 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, trace_api_sta_block_awake(sta->local, pubsta, block); if (block) - set_sta_flags(sta, WLAN_STA_PS_DRIVER); - else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) + set_sta_flag(sta, WLAN_STA_PS_DRIVER); + else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) ieee80211_queue_work(hw, &sta->drv_unblock_wk); } EXPORT_SYMBOL(ieee80211_sta_block_awake); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 348847a32630..8c8ce05ad26f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -19,7 +19,8 @@ /** * enum ieee80211_sta_info_flags - Stations flags * - * These flags are used with &struct sta_info's @flags member. + * These flags are used with &struct sta_info's @flags member, but + * only indirectly with set_sta_flag() and friends. * * @WLAN_STA_AUTH: Station is authenticated. * @WLAN_STA_ASSOC: Station is associated. @@ -53,23 +54,23 @@ * reply to other uAPSD trigger frames or PS-Poll. */ enum ieee80211_sta_info_flags { - WLAN_STA_AUTH = 1<<0, - WLAN_STA_ASSOC = 1<<1, - WLAN_STA_PS_STA = 1<<2, - WLAN_STA_AUTHORIZED = 1<<3, - WLAN_STA_SHORT_PREAMBLE = 1<<4, - WLAN_STA_ASSOC_AP = 1<<5, - WLAN_STA_WME = 1<<6, - WLAN_STA_WDS = 1<<7, - WLAN_STA_CLEAR_PS_FILT = 1<<9, - WLAN_STA_MFP = 1<<10, - WLAN_STA_BLOCK_BA = 1<<11, - WLAN_STA_PS_DRIVER = 1<<12, - WLAN_STA_PSPOLL = 1<<13, - WLAN_STA_TDLS_PEER = 1<<15, - WLAN_STA_TDLS_PEER_AUTH = 1<<16, - WLAN_STA_UAPSD = 1<<17, - WLAN_STA_SP = 1<<18, + WLAN_STA_AUTH, + WLAN_STA_ASSOC, + WLAN_STA_PS_STA, + WLAN_STA_AUTHORIZED, + WLAN_STA_SHORT_PREAMBLE, + WLAN_STA_ASSOC_AP, + WLAN_STA_WME, + WLAN_STA_WDS, + WLAN_STA_CLEAR_PS_FILT, + WLAN_STA_MFP, + WLAN_STA_BLOCK_BA, + WLAN_STA_PS_DRIVER, + WLAN_STA_PSPOLL, + WLAN_STA_TDLS_PEER, + WLAN_STA_TDLS_PEER_AUTH, + WLAN_STA_UAPSD, + WLAN_STA_SP, }; #define STA_TID_NUM 16 @@ -212,10 +213,9 @@ struct sta_ampdu_mlme { * @last_rx_rate_flag: rx status flag of the last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. - * @flaglock: spinlock for flags accesses * @drv_unblock_wk: used for driver PS unblocking * @listen_interval: listen interval of this station, when we're acting as AP - * @flags: STA flags, see &enum ieee80211_sta_info_flags + * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly * @ps_tx_buf: buffers (per AC) of frames to transmit to this station * when it leaves power saving state or polls * @tx_filtered: buffers (per AC) of frames we already tried to @@ -272,7 +272,6 @@ struct sta_info { struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; - spinlock_t flaglock; struct work_struct drv_unblock_wk; @@ -282,11 +281,8 @@ struct sta_info { bool uploaded; - /* - * frequently updated, locked with own spinlock (flaglock), - * use the accessors defined below - */ - u32 flags; + /* use the accessors defined below */ + unsigned long _flags; /* * STA powersave frame queues, no more than the internal @@ -370,60 +366,28 @@ static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) return NL80211_PLINK_LISTEN; } -static inline void set_sta_flags(struct sta_info *sta, const u32 flags) +static inline void set_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - sta->flags |= flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); + set_bit(flag, &sta->_flags); } -static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) +static inline void clear_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - sta->flags &= ~flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); + clear_bit(flag, &sta->_flags); } -static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) +static inline int test_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags & flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; + return test_bit(flag, &sta->_flags); } -static inline u32 test_and_clear_sta_flags(struct sta_info *sta, - const u32 flags) +static inline int test_and_clear_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) { - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags & flags; - sta->flags &= ~flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; -} - -static inline u32 get_sta_flags(struct sta_info *sta) -{ - u32 ret; - unsigned long irqfl; - - spin_lock_irqsave(&sta->flaglock, irqfl); - ret = sta->flags; - spin_unlock_irqrestore(&sta->flaglock, irqfl); - - return ret; + return test_and_clear_bit(flag, &sta->_flags); } void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b5df9be4d043..864a9c3bcf46 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -96,7 +96,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * packet. If the STA went to power save mode, this will happen * when it wakes up for the next time. */ - set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT); + set_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT); /* * This code races in the following way: @@ -132,7 +132,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * changes before calling TX status events if ordering can be * unknown. */ - if (test_sta_flags(sta, WLAN_STA_PS_STA) && + if (test_sta_flag(sta, WLAN_STA_PS_STA) && skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { skb_queue_tail(&sta->tx_filtered[ac], skb); sta_info_recalc_tim(sta); @@ -144,7 +144,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, return; } - if (!test_sta_flags(sta, WLAN_STA_PS_STA) && + if (!test_sta_flag(sta, WLAN_STA_PS_STA) && !(info->flags & IEEE80211_TX_INTFL_RETRIED)) { /* Software retry the packet once */ info->flags |= IEEE80211_TX_INTFL_RETRIED; @@ -157,7 +157,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, wiphy_debug(local->hw.wiphy, "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", skb_queue_len(&sta->tx_filtered[ac]), - !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); + !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); #endif dev_kfree_skb(skb); } @@ -285,10 +285,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) continue; if (info->flags & IEEE80211_TX_STATUS_EOSP) - clear_sta_flags(sta, WLAN_STA_SP); + clear_sta_flag(sta, WLAN_STA_SP); acked = !!(info->flags & IEEE80211_TX_STAT_ACK); - if (!acked && test_sta_flags(sta, WLAN_STA_PS_STA)) { + if (!acked && test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * The STA is in power save mode, so assume * that this TX packet failed because of that. diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 5bf91c43c88c..7699e666457f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -253,7 +253,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - u32 sta_flags; + bool assoc = false; if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; @@ -284,10 +284,11 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (tx->flags & IEEE80211_TX_PS_BUFFERED) return TX_CONTINUE; - sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; + if (tx->sta) + assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); if (likely(tx->flags & IEEE80211_TX_UNICAST)) { - if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && + if (unlikely(!assoc && tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -427,7 +428,7 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, if (!ieee80211_is_mgmt(fc)) return 0; - if (sta == NULL || !test_sta_flags(sta, WLAN_STA_MFP)) + if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP)) return 0; if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) @@ -444,7 +445,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_local *local = tx->local; - u32 staflags; if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control) || @@ -453,9 +453,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) ieee80211_is_reassoc_resp(hdr->frame_control))) return TX_CONTINUE; - staflags = get_sta_flags(sta); - - if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && + if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { int ac = skb_get_queue_mapping(tx->skb); @@ -496,7 +495,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) return TX_QUEUED; } #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - else if (unlikely(staflags & WLAN_STA_PS_STA)) { + else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { printk(KERN_DEBUG "%s: STA %pM in PS mode, but polling/in SP -> send frame\n", tx->sdata->name, sta->sta.addr); @@ -557,7 +556,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!ieee80211_is_robust_mgmt_frame(hdr) || (ieee80211_is_action(hdr->frame_control) && - tx->sta && test_sta_flags(tx->sta, WLAN_STA_MFP)))) { + tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))) { I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); return TX_DROP; } else @@ -616,7 +615,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) u32 len; bool inval = false, rts = false, short_preamble = false; struct ieee80211_tx_rate_control txrc; - u32 sta_flags; + bool assoc = false; memset(&txrc, 0, sizeof(txrc)); @@ -652,17 +651,17 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) */ if (tx->sdata->vif.bss_conf.use_short_preamble && (ieee80211_is_data(hdr->frame_control) || - (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) + (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) txrc.short_preamble = short_preamble = true; - sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; + if (tx->sta) + assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); /* * Lets not bother rate control if we're associated and cannot * talk to the sta. This should not happen. */ - if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && - (sta_flags & WLAN_STA_ASSOC) && + if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc && !rate_usable_index_exists(sband, &tx->sta->sta), "%s: Dropped data frame as no usable bitrate found while " "scanning and associated. Target station: " @@ -1278,7 +1277,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, if (!tx->sta) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - else if (test_and_clear_sta_flags(tx->sta, WLAN_STA_CLEAR_PS_FILT)) + else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT)) info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -1728,7 +1727,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, int encaps_len, skip_header_bytes; int nh_pos, h_pos; struct sta_info *sta = NULL; - u32 sta_flags = 0; + bool wme_sta = false, authorized = false, tdls_auth = false; struct sk_buff *tmp_skb; bool tdls_direct = false; @@ -1754,7 +1753,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr3, skb->data, ETH_ALEN); memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; - sta_flags = get_sta_flags(sta); + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); if (sta) @@ -1843,10 +1843,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, #endif case NL80211_IFTYPE_STATION: if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { + bool tdls_peer = false; + rcu_read_lock(); sta = sta_info_get(sdata, skb->data); - if (sta) - sta_flags = get_sta_flags(sta); + if (sta) { + authorized = test_sta_flag(sta, + WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); + tdls_peer = test_sta_flag(sta, + WLAN_STA_TDLS_PEER); + tdls_auth = test_sta_flag(sta, + WLAN_STA_TDLS_PEER_AUTH); + } rcu_read_unlock(); /* @@ -1854,16 +1863,14 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * directly. Otherwise, allow TDLS setup frames * to be transmitted indirectly. */ - tdls_direct = - (sta_flags & WLAN_STA_TDLS_PEER) && - ((sta_flags & WLAN_STA_TDLS_PEER_AUTH) || + tdls_direct = tdls_peer && (tdls_auth || !(ethertype == ETH_P_TDLS && skb->len > 14 && skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); } if (tdls_direct) { /* link during setup - throw out frames to peer */ - if (!(sta_flags & WLAN_STA_TDLS_PEER_AUTH)) { + if (!tdls_auth) { ret = NETDEV_TX_OK; goto fail; } @@ -1912,17 +1919,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (!is_multicast_ether_addr(hdr.addr1)) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); - if (sta) - sta_flags = get_sta_flags(sta); + if (sta) { + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = test_sta_flag(sta, WLAN_STA_WME); + } rcu_read_unlock(); } /* For mesh, the use of the QoS header is mandatory */ if (ieee80211_vif_is_mesh(&sdata->vif)) - sta_flags |= WLAN_STA_WME; + wme_sta = true; /* receiver and we are QoS enabled, use a QoS type frame */ - if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { + if (wme_sta && local->hw.queues >= 4) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1932,8 +1941,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * EAPOL frames from the local station. */ if (!ieee80211_vif_is_mesh(&sdata->vif) && - unlikely(!is_multicast_ether_addr(hdr.addr1) && - !(sta_flags & WLAN_STA_AUTHORIZED) && + unlikely(!is_multicast_ether_addr(hdr.addr1) && !authorized && !(cpu_to_be16(ethertype) == sdata->control_port_protocol && compare_ether_addr(sdata->vif.addr, skb->data + ETH_ALEN) == 0))) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 60dc600ab65b..7439d26bf5f9 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1122,7 +1122,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) list_for_each_entry(sta, &local->sta_list, list) { ieee80211_sta_tear_down_BA_sessions(sta, true); - clear_sta_flags(sta, WLAN_STA_BLOCK_BA); + clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 971004c9b04f..fd52e695c071 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -72,7 +72,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { - qos = get_sta_flags(sta) & WLAN_STA_WME; + qos = test_sta_flag(sta, WLAN_STA_WME); break; } case NL80211_IFTYPE_AP: @@ -99,7 +99,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) - qos = get_sta_flags(sta) & WLAN_STA_WME; + qos = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); From 5bade101eceedb716e39bd35b2928c465e3fbd10 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:37 +0200 Subject: [PATCH 136/155] mac80211: add missing station flags to debugfs My work and some previous work didn't add all the flags, add them now and while at it simplify the code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/debugfs_sta.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 56bb68b9c42d..c5f341798c16 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -56,19 +56,22 @@ STA_FILE(last_signal, last_signal, D); static ssize_t sta_flags_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[100]; + char buf[121]; struct sta_info *sta = file->private_data; - int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", - test_sta_flag(sta, WLAN_STA_AUTH) ? "AUTH\n" : "", - test_sta_flag(sta, WLAN_STA_ASSOC) ? "ASSOC\n" : "", - test_sta_flag(sta, WLAN_STA_PS_STA) ? "PS (sta)\n" : "", - test_sta_flag(sta, WLAN_STA_PS_DRIVER) ? "PS (driver)\n" : "", - test_sta_flag(sta, WLAN_STA_AUTHORIZED) ? "AUTHORIZED\n" : "", - test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE) ? "SHORT PREAMBLE\n" : "", - test_sta_flag(sta, WLAN_STA_WME) ? "WME\n" : "", - test_sta_flag(sta, WLAN_STA_WDS) ? "WDS\n" : "", - test_sta_flag(sta, WLAN_STA_MFP) ? "MFP\n" : ""); +#define TEST(flg) \ + test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" + + int res = scnprintf(buf, sizeof(buf), + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + TEST(AUTH), TEST(ASSOC), TEST(PS_STA), + TEST(PS_DRIVER), TEST(AUTHORIZED), + TEST(SHORT_PREAMBLE), TEST(ASSOC_AP), + TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT), + TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), + TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), + TEST(TDLS_PEER_AUTH)); +#undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); From 40b96408831f038b1a6b45e8b22cd050f82a3896 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:38 +0200 Subject: [PATCH 137/155] mac80211: explicitly notify drivers of frame release iwlwifi needs to know the number of frames that are going to be sent to a station while it is asleep so it can properly handle the uCode blocking of that station. Before uAPSD, we got by by telling the device that a single frame was going to be released whenever we encountered IEEE80211_TX_CTL_POLL_RESPONSE. With uAPSD, however, that is no longer possible since there could be more than a single frame. To support this model, add a new callback to notify drivers when frames are going to be released. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 17 +++++++++++++++++ net/mac80211/driver-ops.h | 15 +++++++++++++++ net/mac80211/driver-trace.h | 22 +++++++++++++++++++++- net/mac80211/sta_info.c | 34 +++++++++++++++++++++++++--------- 4 files changed, 78 insertions(+), 10 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ee6449eff7dc..778572d38bd3 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1973,6 +1973,18 @@ enum ieee80211_frame_release_type { * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP * on the last frame in the SP. * This callback must be atomic. + * @allow_buffered_frames: Prepare device to allow the given number of frames + * to go out to the given station. The frames will be sent by mac80211 + * via the usual TX path after this call. The TX information for frames + * released will also have the %IEEE80211_TX_CTL_POLL_RESPONSE flag set + * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case + * frames from multiple TIDs are released and the driver might reorder + * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag + * on the last frame and clear it on all others and also handle the EOSP + * bit in the QoS header correctly. + * The @tids parameter is a bitmap and tells the driver which TIDs the + * frames will be on; it will at most have two bits set. + * This callback must be atomic. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2088,6 +2100,11 @@ struct ieee80211_ops { void (*rssi_callback)(struct ieee80211_hw *hw, enum ieee80211_rssi_event rssi_event); + void (*allow_buffered_frames)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data); void (*release_buffered_frames)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tids, int num_frames, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 8fa0d2edf54c..68721d379fe1 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -685,4 +685,19 @@ drv_release_buffered_frames(struct ieee80211_local *local, more_data); trace_drv_return_void(local); } + +static inline void +drv_allow_buffered_frames(struct ieee80211_local *local, + struct sta_info *sta, u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + trace_drv_allow_buffered_frames(local, &sta->sta, tids, num_frames, + reason, more_data); + if (local->ops->allow_buffered_frames) + local->ops->allow_buffered_frames(&local->hw, &sta->sta, + tids, num_frames, reason, + more_data); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 531fbd086794..aef08969e353 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1129,7 +1129,7 @@ TRACE_EVENT(drv_rssi_callback, ) ); -TRACE_EVENT(drv_release_buffered_frames, +DECLARE_EVENT_CLASS(release_evt, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, u16 tids, int num_frames, @@ -1164,6 +1164,26 @@ TRACE_EVENT(drv_release_buffered_frames, ) ); +DEFINE_EVENT(release_evt, drv_release_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + +DEFINE_EVENT(release_evt, drv_allow_buffered_frames, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, + u16 tids, int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data), + + TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index a00358224cd5..907b42081f3c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1169,7 +1169,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, int tid, - bool uapsd) + enum ieee80211_frame_release_type reason) { struct ieee80211_local *local = sdata->local; struct ieee80211_qos_hdr *nullfunc; @@ -1210,7 +1210,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, nullfunc->qos_ctrl = cpu_to_le16(tid); - if (uapsd) + if (reason == IEEE80211_FRAME_RELEASE_UAPSD) nullfunc->qos_ctrl |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP); } @@ -1227,6 +1227,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + ieee80211_xmit(sdata, skb); } @@ -1324,20 +1326,24 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* This will evaluate to 1, 3, 5 or 7. */ tid = 7 - ((ffs(~ignored_acs) - 1) << 1); - ieee80211_send_null_response(sdata, sta, tid, - reason == IEEE80211_FRAME_RELEASE_UAPSD); + ieee80211_send_null_response(sdata, sta, tid, reason); return; } if (!driver_release_tids) { struct sk_buff_head pending; struct sk_buff *skb; + int num = 0; + u16 tids = 0; skb_queue_head_init(&pending); while ((skb = __skb_dequeue(&frames))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *) skb->data; + u8 *qoshdr = NULL; + + num++; /* * Tell TX path to send this frame even though the @@ -1357,19 +1363,29 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + if (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) + qoshdr = ieee80211_get_qos_ctl(hdr); + + /* set EOSP for the frame */ if (reason == IEEE80211_FRAME_RELEASE_UAPSD && - skb_queue_empty(&frames)) { - /* set EOSP for the frame */ - u8 *p = ieee80211_get_qos_ctl(hdr); - *p |= IEEE80211_QOS_CTL_EOSP; - } + qoshdr && skb_queue_empty(&frames)) + *qoshdr |= IEEE80211_QOS_CTL_EOSP; info->flags |= IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + if (qoshdr) + tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); + else + tids |= BIT(0); + __skb_queue_tail(&pending, skb); } + drv_allow_buffered_frames(local, sta, tids, num, + reason, more_data); + ieee80211_add_pending_skbs(local, &pending); sta_info_recalc_tim(sta); From 37fbd9080088f5f98ab81a6f2ad456857971a089 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:39 +0200 Subject: [PATCH 138/155] mac80211: allow out-of-band EOSP notification iwlwifi has a separate EOSP notification from the device, and to make use of that properly it needs to be passed to mac80211. To be able to mix with tx_status_irqsafe and rx_irqsafe it also needs to be an "_irqsafe" version in the sense that it goes through the tasklet, the actual flag clearing would be IRQ-safe but doing it directly would cause reordering issues. This is needed in the case of a P2P GO going into an absence period without transmitting any frames that should be driver-released as in this case there's no other way to inform mac80211 that the service period ended. Note that for drivers that don't use the _irqsafe functions another version of this function will be required. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 24 ++++++++++++++++++++++-- net/mac80211/driver-trace.h | 22 ++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 5 +++++ net/mac80211/main.c | 14 ++++++++++++++ net/mac80211/sta_info.c | 25 +++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 778572d38bd3..3df32a04402c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1971,7 +1971,8 @@ enum ieee80211_frame_release_type { * at least one, however). In this case it is also responsible for * setting the EOSP flag in the QoS header of the frames. Also, when the * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP - * on the last frame in the SP. + * on the last frame in the SP. Alternatively, it may call the function + * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. * This callback must be atomic. * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 @@ -1981,7 +1982,8 @@ enum ieee80211_frame_release_type { * frames from multiple TIDs are released and the driver might reorder * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag * on the last frame and clear it on all others and also handle the EOSP - * bit in the QoS header correctly. + * bit in the QoS header correctly. Alternatively, it can also call the + * ieee80211_sta_eosp_irqsafe() function. * The @tids parameter is a bitmap and tells the driver which TIDs the * frames will be on; it will at most have two bits set. * This callback must be atomic. @@ -3112,6 +3114,24 @@ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, void ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, bool block); +/** + * ieee80211_sta_eosp - notify mac80211 about end of SP + * @pubsta: the station + * + * When a device transmits frames in a way that it can't tell + * mac80211 in the TX status about the EOSP, it must clear the + * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. + * This applies for PS-Poll as well as uAPSD. + * + * Note that there is no non-_irqsafe version right now as + * it wasn't needed, but just like _tx_status() and _rx() + * must not be mixed in irqsafe/non-irqsafe versions, this + * function must not be mixed with those either. Use the + * all irqsafe, or all non-irqsafe, don't mix! If you need + * the non-irqsafe version of this, you need to add it. + */ +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); + /** * ieee80211_iter_keys - iterate keys programmed into the device * @hw: pointer obtained from ieee80211_alloc_hw() diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index aef08969e353..2af4fca55337 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1498,6 +1498,28 @@ TRACE_EVENT(api_enable_rssi_reports, ) ); +TRACE_EVENT(api_eosp, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta), + + TP_ARGS(local, sta), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT, + LOCAL_PR_ARG, STA_PR_FMT + ) +); + /* * Tracing for internal functions * (which may also be called in response to driver calls) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index da3206450192..9fa5f8a674bc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -664,6 +664,11 @@ enum sdata_queue_type { enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, + IEEE80211_EOSP_MSG = 3, +}; + +struct skb_eosp_msg_data { + u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 336ceb9d2462..17b038aeac9b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -325,6 +325,8 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sta_info *sta, *tmp; + struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -340,6 +342,18 @@ static void ieee80211_tasklet_handler(unsigned long data) skb->pkt_type = 0; ieee80211_tx_status(local_to_hw(local), skb); break; + case IEEE80211_EOSP_MSG: + eosp_data = (void *)skb->cb; + for_each_sta_info(local, eosp_data->sta, sta, tmp) { + /* skip wrong virtual interface */ + if (memcmp(eosp_data->iface, + sta->sdata->vif.addr, ETH_ALEN)) + continue; + clear_sta_flag(sta, WLAN_STA_SP); + break; + } + dev_kfree_skb(skb); + break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 907b42081f3c..076593bffbcf 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1478,6 +1478,31 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_sta_block_awake); +void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_local *local = sta->local; + struct sk_buff *skb; + struct skb_eosp_msg_data *data; + + trace_api_eosp(local, pubsta); + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) { + /* too bad ... but race is better than loss */ + clear_sta_flag(sta, WLAN_STA_SP); + return; + } + + data = (void *)skb->cb; + memcpy(data->sta, pubsta->addr, ETH_ALEN); + memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); + skb->pkt_type = IEEE80211_EOSP_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); +} +EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); + void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, u8 tid, bool buffered) { From 4b801bc969364a980c1366e48155d1a29d20661b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:40 +0200 Subject: [PATCH 139/155] mac80211: document client powersave With the addition of uAPSD and driver buffering the powersave handling has gotten quite complex. Add a section to the documentation to explain it for anyone wanting to implement it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 11 +++- include/net/mac80211.h | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 445289cd0e65..2014155c899d 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -433,8 +433,18 @@ Insert notes about VLAN interfaces with hw crypto here or in the hw crypto chapter. +
+ support for powersaving clients +!Pinclude/net/mac80211.h AP support for powersaving clients +
!Finclude/net/mac80211.h ieee80211_get_buffered_bc !Finclude/net/mac80211.h ieee80211_beacon_get +!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe +!Finclude/net/mac80211.h ieee80211_frame_release_type +!Finclude/net/mac80211.h ieee80211_sta_ps_transition +!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni +!Finclude/net/mac80211.h ieee80211_sta_set_buffered +!Finclude/net/mac80211.h ieee80211_sta_block_awake @@ -460,7 +470,6 @@ !Finclude/net/mac80211.h sta_notify_cmd !Finclude/net/mac80211.h ieee80211_find_sta !Finclude/net/mac80211.h ieee80211_find_sta_by_ifaddr -!Finclude/net/mac80211.h ieee80211_sta_block_awake diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3df32a04402c..bc799304be71 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1538,6 +1538,95 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * This rule applies to all other FIF flags as well. */ +/** + * DOC: AP support for powersaving clients + * + * In order to implement AP and P2P GO modes, mac80211 has support for + * client powersaving, both "legacy" PS (PS-Poll/null data) and uAPSD. + * There currently is no support for sAPSD. + * + * There is one assumption that mac80211 makes, namely that a client + * will not poll with PS-Poll and trigger with uAPSD at the same time. + * Both are supported, and both can be used by the same client, but + * they can't be used concurrently by the same client. This simplifies + * the driver code. + * + * The first thing to keep in mind is that there is a flag for complete + * driver implementation: %IEEE80211_HW_AP_LINK_PS. If this flag is set, + * mac80211 expects the driver to handle most of the state machine for + * powersaving clients and will ignore the PM bit in incoming frames. + * Drivers then use ieee80211_sta_ps_transition() to inform mac80211 of + * stations' powersave transitions. In this mode, mac80211 also doesn't + * handle PS-Poll/uAPSD. + * + * In the mode without %IEEE80211_HW_AP_LINK_PS, mac80211 will check the + * PM bit in incoming frames for client powersave transitions. When a + * station goes to sleep, we will stop transmitting to it. There is, + * however, a race condition: a station might go to sleep while there is + * data buffered on hardware queues. If the device has support for this + * it will reject frames, and the driver should give the frames back to + * mac80211 with the %IEEE80211_TX_STAT_TX_FILTERED flag set which will + * cause mac80211 to retry the frame when the station wakes up. The + * driver is also notified of powersave transitions by calling its + * @sta_notify callback. + * + * When the station is asleep, it has three choices: it can wake up, + * it can PS-Poll, or it can possibly start a uAPSD service period. + * Waking up is implemented by simply transmitting all buffered (and + * filtered) frames to the station. This is the easiest case. When + * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 + * will inform the driver of this with the @allow_buffered_frames + * callback; this callback is optional. mac80211 will then transmit + * the frames as usual and set the %IEEE80211_TX_CTL_POLL_RESPONSE + * on each frame. The last frame in the service period (or the only + * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to + * indicate that it ends the service period; as this frame must have + * TX status report it also sets %IEEE80211_TX_CTL_REQ_TX_STATUS. + * When TX status is reported for this frame, the service period is + * marked has having ended and a new one can be started by the peer. + * + * Another race condition can happen on some devices like iwlwifi + * when there are frames queued for the station and it wakes up + * or polls; the frames that are already queued could end up being + * transmitted first instead, causing reordering and/or wrong + * processing of the EOSP. The cause is that allowing frames to be + * transmitted to a certain station is out-of-band communication to + * the device. To allow this problem to be solved, the driver can + * call ieee80211_sta_block_awake() if frames are buffered when it + * is notified that the station went to sleep. When all these frames + * have been filtered (see above), it must call the function again + * to indicate that the station is no longer blocked. + * + * If the driver buffers frames in the driver for aggregation in any + * way, it must use the ieee80211_sta_set_buffered() call when it is + * notified of the station going to sleep to inform mac80211 of any + * TIDs that have frames buffered. Note that when a station wakes up + * this information is reset (hence the requirement to call it when + * informed of the station going to sleep). Then, when a service + * period starts for any reason, @release_buffered_frames is called + * with the number of frames to be released and which TIDs they are + * to come from. In this case, the driver is responsible for setting + * the EOSP (for uAPSD) and MORE_DATA bits in the released frames, + * to help the @more_data paramter is passed to tell the driver if + * there is more data on other TIDs -- the TIDs to release frames + * from are ignored since mac80211 doesn't know how many frames the + * buffers for those TIDs contain. + * + * If the driver also implement GO mode, where absence periods may + * shorten service periods (or abort PS-Poll responses), it must + * filter those response frames except in the case of frames that + * are buffered in the driver -- those must remain buffered to avoid + * reordering. Because it is possible that no frames are released + * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * to indicate to mac80211 that the service period ended anyway. + * + * Finally, if frames from multiple TIDs are released from mac80211 + * but the driver might reorder them, it must clear & set the flags + * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) + * and also take care of the EOSP and MORE_DATA bits in the frame. + * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + */ + /** * enum ieee80211_filter_flags - hardware filter flags * From 49a59543eb5a5d268b3d11747f9c3c557ae271a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 29 Sep 2011 16:04:41 +0200 Subject: [PATCH 140/155] mac80211: dont assign seqno to or aggregate QoS Null frames 802.11 says: "Sequence numbers for QoS (+)Null frames may be set to any value." However, if we use the normal counters then peers will get confused with aggregation since there'll be holes in the sequence number sequence. To avoid that, neither assign a sequence number to QoS null frames nor put them on aggregation. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7699e666457f..ae5dd85f1e93 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -804,6 +804,9 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) if (ieee80211_hdrlen(hdr->frame_control) < 24) return TX_CONTINUE; + if (ieee80211_is_qos_nullfunc(hdr->frame_control)) + return TX_CONTINUE; + /* * Anything but QoS data that has a sequence number field * (is long enough) gets a sequence number from the global @@ -1236,6 +1239,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->sta = sta_info_get(sdata, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && + !ieee80211_is_qos_nullfunc(hdr->frame_control) && (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) && !(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) { struct tid_ampdu_tx *tid_tx; From ef548626429531fedae9ae44c1e89e14cf3244f7 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sat, 1 Oct 2011 09:43:09 +0300 Subject: [PATCH 141/155] ath6kl: fix size_t related warnings My earlier debug log additions added these warnings when compiling 64 bit kernels: ath6kl/init.c:962: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' ath6kl/init.c:975: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' ath6kl/init.c:988: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' ath6kl/init.c:1009: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' ath6kl/init.c:1192: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' ath6kl/init.c:1236: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' ath6kl/init.c:1267: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' Reported-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 5995bb9ead8d..c1d2366704b5 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -959,7 +959,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) switch (ie_id) { case ATH6KL_FW_IE_OTP_IMAGE: - ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%d B)\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", ie_len); ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); @@ -972,7 +972,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->fw_otp_len = ie_len; break; case ATH6KL_FW_IE_FW_IMAGE: - ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%d B)\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n", ie_len); ar->fw = kmemdup(data, ie_len, GFP_KERNEL); @@ -985,7 +985,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) ar->fw_len = ie_len; break; case ATH6KL_FW_IE_PATCH_IMAGE: - ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%d B)\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n", ie_len); ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); @@ -1007,7 +1007,7 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar) break; case ATH6KL_FW_IE_CAPABILITIES: ath6kl_dbg(ATH6KL_DBG_BOOT, - "found firmware capabilities ie (%d B)\n", + "found firmware capabilities ie (%zd B)\n", ie_len); for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { @@ -1189,7 +1189,7 @@ static int ath6kl_upload_otp(struct ath6kl *ar) address = ar->hw.app_load_addr; - ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%d B)\n", address, + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address, ar->fw_otp_len); ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp, @@ -1233,7 +1233,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar) address = ar->hw.app_load_addr; - ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%d B)\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n", address, ar->fw_len); ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len); @@ -1264,7 +1264,7 @@ static int ath6kl_upload_patch(struct ath6kl *ar) address = ar->hw.dataset_patch_addr; - ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%d B)\n", + ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n", address, ar->fw_patch_len); ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len); From 62c83ac4d6bcfa6a116c8f1c8ace05cb3933a4f1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 3 Oct 2011 13:44:40 +0300 Subject: [PATCH 142/155] ath6kl: include vmalloc.h in debug.c Fixes compilation errors when compiling for ARM: ath6kl/debug.c:312: error: implicit declaration of function 'vmalloc' ath6kl/debug.c:312: warning: assignment makes pointer from integer without a cast ath6kl/debug.c:342: error: implicit declaration of function 'vfree' ath6kl/debug.c:696: warning: assignment makes pointer from integer without a cast ath6kl/debug.c:871: warning: assignment makes pointer from integer without a cast Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index 5237369cd521..ba3f23d71150 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -18,6 +18,7 @@ #include #include +#include #include "debug.h" #include "target.h" From 893d73f4a15bda966cb72f84897898eb235e134c Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 29 Sep 2011 13:42:25 +0200 Subject: [PATCH 143/155] mac80211: Allow noack flag overwrite for injected frames Allow injected unicast frames to be sent without having to wait for an ACK. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- include/net/ieee80211_radiotap.h | 1 + net/mac80211/tx.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index b0be5fb9de19..7e2c4d483ad0 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -251,6 +251,7 @@ enum ieee80211_radiotap_type { * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ack */ /* For IEEE80211_RADIOTAP_MCS */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ae5dd85f1e93..ad2ee4a90ec4 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1054,6 +1054,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); + u16 txflags; info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; tx->flags &= ~IEEE80211_TX_FRAGMENTED; @@ -1102,6 +1103,13 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, tx->flags |= IEEE80211_TX_FRAGMENTED; break; + case IEEE80211_RADIOTAP_TX_FLAGS: + txflags = le16_to_cpu(get_unaligned((__le16*) + iterator.this_arg)); + if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + break; + /* * Please update the file * Documentation/networking/mac80211-injection.txt @@ -1266,8 +1274,11 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->flags |= IEEE80211_TX_UNICAST; if (unlikely(local->wifi_wme_noack_test)) info->flags |= IEEE80211_TX_CTL_NO_ACK; - else - info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + /* + * Flags are initialized to 0. Hence, no need to + * explicitly unset IEEE80211_TX_CTL_NO_ACK since + * it might already be set for injected frames. + */ } if (tx->flags & IEEE80211_TX_FRAGMENTED) { From e209c5a7ed1870ab7f112ad47083b5d616e8b6a4 Mon Sep 17 00:00:00 2001 From: Sangwook Lee Date: Thu, 29 Sep 2011 12:57:17 +0100 Subject: [PATCH 144/155] net:rfkill: add a gpio setup function into GPIO rfkill Add a gpio setup function which gives a chance to set up platform specific configuration such as pin multiplexing, input/output direction at the runtime or booting time. Signed-off-by: Sangwook Lee Signed-off-by: John W. Linville --- include/linux/rfkill-gpio.h | 4 ++++ net/rfkill/rfkill-gpio.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h index a175d0598033..4d09f6eab359 100644 --- a/include/linux/rfkill-gpio.h +++ b/include/linux/rfkill-gpio.h @@ -30,6 +30,8 @@ * @reset_gpio: GPIO which is used for reseting rfkill switch * @shutdown_gpio: GPIO which is used for shutdown of rfkill switch * @power_clk_name: [optional] name of clk to turn off while blocked + * @gpio_runtime_close: clean up platform specific gpio configuration + * @gpio_runtime_setup: set up platform specific gpio configuration */ struct rfkill_gpio_platform_data { @@ -38,6 +40,8 @@ struct rfkill_gpio_platform_data { int shutdown_gpio; const char *power_clk_name; enum rfkill_type type; + void (*gpio_runtime_close)(struct platform_device *); + int (*gpio_runtime_setup)(struct platform_device *); }; #endif /* __RFKILL_GPIO_H */ diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 256c5ddd2d72..128677d69056 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -101,6 +101,14 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (!rfkill) return -ENOMEM; + if (pdata->gpio_runtime_setup) { + ret = pdata->gpio_runtime_setup(pdev); + if (ret) { + pr_warn("%s: can't set up gpio\n", __func__); + return ret; + } + } + rfkill->pdata = pdata; len = strlen(pdata->name); @@ -182,7 +190,10 @@ fail_alloc: static int rfkill_gpio_remove(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); + struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; + if (pdata->gpio_runtime_close) + pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) From b6f35301efda5e94342cfcca9e29b7b3e9a5f827 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 29 Sep 2011 20:34:04 +0530 Subject: [PATCH 145/155] mac80211: Send nullfunc frames at lower rate during connection monitor Recently mac80211 was changed to use nullfunc instead of probe request for connection monitoring for tx ack status reporting hardwares. Sometimes in congested network, STA got disconnected quickly after the association. It was observered that the rate control was not adopted to environment due to minimal transmission. As the nullfunc are used for monitoring purpose, these frames should not be sacrificed for rate control updation. So it is better to send the monitoring null func frames at minimum rate that could help to retain the connection. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++++ net/mac80211/mlme.c | 5 +++++ net/mac80211/rate.c | 8 +++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bc799304be71..135e897b61c7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -372,6 +372,9 @@ struct ieee80211_bss_conf { * an SP that mac80211 transmits, it is already set; for driver frames * the driver may set this flag. It is also used to do the same for * PS-Poll responses. + * @IEEE80211_TX_CTL_USE_MINRATE: This frame will be sent at lowest rate. + * This flag is used to send nullfunc frame at minimum rate when + * the nullfunc is used for connection monitoring purpose. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -404,6 +407,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), IEEE80211_TX_STATUS_EOSP = BIT(28), + IEEE80211_TX_CTL_USE_MINRATE = BIT(29), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c4e8901c96f6..0e5d8daba1ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -348,6 +348,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, { struct sk_buff *skb; struct ieee80211_hdr_3addr *nullfunc; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif); if (!skb) @@ -358,6 +359,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; + ieee80211_tx_skb(sdata, skb); } diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index f61244c0e0a2..ff5c3aa48a15 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -199,7 +199,7 @@ static void rate_control_release(struct kref *kref) kfree(ctrl_ref); } -static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) +static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -208,7 +208,9 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) fc = hdr->frame_control; - return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc); + return (info->flags & (IEEE80211_TX_CTL_NO_ACK | + IEEE80211_TX_CTL_USE_MINRATE)) || + !ieee80211_is_data(fc); } static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, @@ -262,7 +264,7 @@ bool rate_control_send_low(struct ieee80211_sta *sta, struct ieee80211_supported_band *sband = txrc->sband; int mcast_rate; - if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { + if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { if ((sband->band != IEEE80211_BAND_2GHZ) || !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) info->control.rates[0].idx = From 8c34559b4a6df32e4af1b073397fa4dc189a5485 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Sep 2011 10:42:19 -0700 Subject: [PATCH 146/155] ath9k_htc: add AVM FRITZ!WLAN 11N v2 support This was reported and tested by Martin Walter over at AVM GmbH Berlin. This also applies to 3.0.1 so sendint to stable. Cc: s.kirste@avm.de Cc: d.friedel@avm.de Cc: Martin Walter Cc: Peter Grabienski Cc: stable@kernel.org Tested-by: Martin Walter Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hif_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index d3f4a59cd456..77c8ded8de57 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -38,6 +38,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x04CA, 0x4605) }, /* Liteon */ { USB_DEVICE(0x040D, 0x3801) }, /* VIA */ { USB_DEVICE(0x0cf3, 0xb003) }, /* Ubiquiti WifiStation Ext */ + { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ From d85c5fe462fe3531f607fda787e9c80617e35437 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 29 Sep 2011 20:43:40 -0700 Subject: [PATCH 147/155] mwifiex: correct AMSDU aggregation check The commit "mwifiex: remove list traversal.."(fcf2176c87..) wrongly modifies AMSDU aggregation check. Due to this even though packet size for iperf traffic is already large, we unnecessarily try to aggregate them which adds some delay. If Tx iperf is started on UUT for 30 seconds, UUT keeps sending Tx packets for few more seconds. That commit is reverted to fix the problem. Also, MIN_NUM_AMSDU check is moved inside the loop to optimize the loop. Signed-off-by: Amitkumar Karwar Signed-off-by: Yogesh Ashok Powar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n_aggr.c | 2 -- drivers/net/wireless/mwifiex/11n_aggr.h | 1 + drivers/net/wireless/mwifiex/main.h | 1 - drivers/net/wireless/mwifiex/wmm.c | 32 ++++++++++++++++++------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 1a453a605b3f..9e63d16365e3 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -193,7 +193,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_src = skb_dequeue(&pra_list->skb_head); pra_list->total_pkts_size -= skb_src->len; - pra_list->total_pkts--; atomic_dec(&priv->wmm.tx_pkts_queued); @@ -269,7 +268,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_queue_tail(&pra_list->skb_head, skb_aggr); pra_list->total_pkts_size += skb_aggr->len; - pra_list->total_pkts++; atomic_inc(&priv->wmm.tx_pkts_queued); diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h index 9c6dca7ab02c..900e1c62a0cc 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.h +++ b/drivers/net/wireless/mwifiex/11n_aggr.h @@ -21,6 +21,7 @@ #define _MWIFIEX_11N_AGGR_H_ #define PKT_TYPE_AMSDU 0xE6 +#define MIN_NUM_AMSDU 2 int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, struct sk_buff *skb); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 4f4042809f23..907ab746dc4b 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -173,7 +173,6 @@ struct mwifiex_ra_list_tbl { struct sk_buff_head skb_head; u8 ra[ETH_ALEN]; u32 total_pkts_size; - u32 total_pkts; u32 is_11n_enabled; }; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 69e260b41711..eda24474c1fc 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -121,7 +121,6 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) memcpy(ra_list->ra, ra, ETH_ALEN); ra_list->total_pkts_size = 0; - ra_list->total_pkts = 0; dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list); @@ -648,7 +647,6 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter, skb_queue_tail(&ra_list->skb_head, skb); ra_list->total_pkts_size += skb->len; - ra_list->total_pkts++; atomic_inc(&priv->wmm.tx_pkts_queued); @@ -974,6 +972,28 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, return NULL; } +/* + * This function checks if 11n aggregation is possible. + */ +static int +mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, + int max_buf_size) +{ + int count = 0, total_size = 0; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { + total_size += skb->len; + if (total_size >= max_buf_size) + break; + if (++count >= MIN_NUM_AMSDU) + return true; + } + + return false; +} + /* * This function sends a single packet to firmware for transmission. */ @@ -1001,7 +1021,6 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb); ptr->total_pkts_size -= skb->len; - ptr->total_pkts--; if (!skb_queue_empty(&ptr->skb_head)) skb_next = skb_peek(&ptr->skb_head); @@ -1027,7 +1046,6 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, skb_queue_tail(&ptr->skb_head, skb); ptr->total_pkts_size += skb->len; - ptr->total_pkts++; tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); @@ -1213,11 +1231,9 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_send_delba(priv, tid_del, ra, 1); } } -/* Minimum number of AMSDU */ -#define MIN_NUM_AMSDU 2 - if (mwifiex_is_amsdu_allowed(priv, tid) && - (ptr->total_pkts >= MIN_NUM_AMSDU)) + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, ptr_index, flags); /* ra_list_spinlock has been freed in From 44b815c6b063ddadacc062a28a3e3facc8486f31 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 29 Sep 2011 20:43:41 -0700 Subject: [PATCH 148/155] mwifiex: handle an error path correctly In failure case locks are not allocated in mwifiex_register(). So mwifiex_free_lock_list() routine call becomes redundant. Also we don't need to check return type for mwifiex_init_lock_list() routine. It never fails. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 8b05b4f5ffe2..848645118ad4 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -75,8 +75,7 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, adapter->priv_num++; adapter->priv[0]->adapter = adapter; - if (mwifiex_init_lock_list(adapter)) - goto error; + mwifiex_init_lock_list(adapter); init_timer(&adapter->cmd_timer); adapter->cmd_timer.function = mwifiex_cmd_timeout_func; @@ -87,8 +86,6 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, error: dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n"); - mwifiex_free_lock_list(adapter); - for (i = 0; i < adapter->priv_num; i++) kfree(adapter->priv[i]); From 28d8c1df03502918bcafed38a0ccdca090b3a38b Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 30 Sep 2011 12:17:28 +0530 Subject: [PATCH 149/155] ath9k_hw: extend GPIO pin select mask for rfkill this extends the bits for rf kill GPIO selection to [7:2] from [4:2] as we use GPIO pin 11 as rfkill for AR9480 and also remove few unused macros Cc: Wilson Tsao Cc: "Hu, Russell" Cc: Rajkumar Manoharan Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index a3c7d0c247a3..5d92f96980e6 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -104,16 +104,11 @@ #define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_11_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) -#define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c -#define AR_EEPROM_RFSILENT_GPIO_SEL_S 2 -#define AR_EEPROM_RFSILENT_POLARITY 0x0002 -#define AR_EEPROM_RFSILENT_POLARITY_S 1 - #define EEP_RFSILENT_ENABLED 0x0001 #define EEP_RFSILENT_ENABLED_S 0 #define EEP_RFSILENT_POLARITY 0x0002 #define EEP_RFSILENT_POLARITY_S 1 -#define EEP_RFSILENT_GPIO_SEL 0x001c +#define EEP_RFSILENT_GPIO_SEL (AR_SREV_9480(ah) ? 0x00fc : 0x001c) #define EEP_RFSILENT_GPIO_SEL_S 2 #define AR5416_OPFLAGS_11A 0x01 From 3c607d27c818cf4a5d28f2c73b18a88f8fbdfa33 Mon Sep 17 00:00:00 2001 From: Don Fry Date: Fri, 30 Sep 2011 11:40:20 -0700 Subject: [PATCH 150/155] iwlagn: rename iwlagn module iwlwifi and alias to iwlagn. Rename the iwlagn module as iwlwifi in preparation for future changes. Add an alias to iwlagn for backward compatibility. Signed-off-by: Don Fry Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 5 +++ drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/iwlwifi/Kconfig | 22 ++++++------ drivers/net/wireless/iwlwifi/Makefile | 40 +++++++++++----------- drivers/net/wireless/iwlwifi/iwl-agn.c | 1 + drivers/net/wireless/iwlwifi/iwl-shared.h | 2 +- 6 files changed, 39 insertions(+), 33 deletions(-) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index dfd6a9f4a583..1cf3dbdb1538 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -555,3 +555,8 @@ Why: This driver has been superseded by g_mass_storage. Who: Alan Stern ---------------------------- +What: iwlagn alias support +When: 3.5 +Why: The iwlagn module has been renamed iwlwifi. The alias will be around + for backward compatibility for several cycles and then dropped. +Who: Don Fry \ No newline at end of file diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7bba6a82b875..4cf0ad312da1 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -41,7 +41,7 @@ obj-$(CONFIG_ADM8211) += adm8211.o obj-$(CONFIG_MWL8K) += mwl8k.o -obj-$(CONFIG_IWLAGN) += iwlwifi/ +obj-$(CONFIG_IWLWIFI) += iwlwifi/ obj-$(CONFIG_IWLWIFI_LEGACY) += iwlegacy/ obj-$(CONFIG_RT2X00) += rt2x00/ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 1d7572f9887f..e0441033788c 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -1,5 +1,5 @@ -config IWLAGN - tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlagn) " +config IWLWIFI + tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " depends on PCI && MAC80211 select FW_LOADER select NEW_LEDS @@ -39,14 +39,14 @@ config IWLAGN If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The - module will be called iwlagn. + module will be called iwlwifi. menu "Debugging Options" - depends on IWLAGN + depends on IWLWIFI config IWLWIFI_DEBUG - bool "Enable full debugging output in the iwlagn driver" - depends on IWLAGN + bool "Enable full debugging output in the iwlwifi driver" + depends on IWLWIFI ---help--- This option will enable debug tracing output for the iwlwifi drivers @@ -70,8 +70,8 @@ config IWLWIFI_DEBUG any problems you may encounter. config IWLWIFI_DEBUGFS - bool "iwlagn debugfs support" - depends on IWLAGN && MAC80211_DEBUGFS + bool "iwlwifi debugfs support" + depends on IWLWIFI && MAC80211_DEBUGFS ---help--- Enable creation of debugfs files for the iwlwifi drivers. This is a low-impact option that allows getting insight into the @@ -79,13 +79,13 @@ config IWLWIFI_DEBUGFS config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE bool "Experimental uCode support" - depends on IWLAGN && IWLWIFI_DEBUG + depends on IWLWIFI && IWLWIFI_DEBUG ---help--- Enable use of experimental ucode for testing and debugging. config IWLWIFI_DEVICE_TRACING bool "iwlwifi device access tracing" - depends on IWLAGN + depends on IWLWIFI depends on EVENT_TRACING help Say Y here to trace all commands, including TX frames and IO @@ -104,7 +104,7 @@ endmenu config IWLWIFI_DEVICE_SVTOOL bool "iwlwifi device svtool support" - depends on IWLAGN + depends on IWLWIFI select NL80211_TESTMODE help This option enables the svtool support for iwlwifi device through diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 8fa59cdb3b49..bacafa4a5f48 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,25 +1,25 @@ -# AGN -obj-$(CONFIG_IWLAGN) += iwlagn.o -iwlagn-objs := iwl-agn.o iwl-agn-rs.o -iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o -iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o -iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o +# WIFI +obj-$(CONFIG_IWLWIFI) += iwlwifi.o +iwlwifi-objs := iwl-agn.o iwl-agn-rs.o +iwlwifi-objs += iwl-agn-ucode.o iwl-agn-tx.o +iwlwifi-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o +iwlwifi-objs += iwl-agn-tt.o iwl-agn-sta.o -iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o -iwlagn-objs += iwl-rx.o iwl-sta.o -iwlagn-objs += iwl-scan.o iwl-led.o -iwlagn-objs += iwl-agn-rxon.o -iwlagn-objs += iwl-5000.o -iwlagn-objs += iwl-6000.o -iwlagn-objs += iwl-1000.o -iwlagn-objs += iwl-2000.o -iwlagn-objs += iwl-pci.o -iwlagn-objs += iwl-trans.o -iwlagn-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o +iwlwifi-objs += iwl-core.o iwl-eeprom.o iwl-power.o +iwlwifi-objs += iwl-rx.o iwl-sta.o +iwlwifi-objs += iwl-scan.o iwl-led.o +iwlwifi-objs += iwl-agn-rxon.o +iwlwifi-objs += iwl-5000.o +iwlwifi-objs += iwl-6000.o +iwlwifi-objs += iwl-1000.o +iwlwifi-objs += iwl-2000.o +iwlwifi-objs += iwl-pci.o +iwlwifi-objs += iwl-trans.o +iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o -iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o -iwlagn-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -iwlagn-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o +iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o +iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o +iwlwifi-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o CFLAGS_iwl-devtrace.o := -I$(src) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index baaf48616cc7..d0fd6f063bf8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -79,6 +79,7 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +MODULE_ALIAS("iwlagn"); void iwl_update_chain_flags(struct iwl_priv *priv) { diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index 8747bbdf8983..3a24b477b8fb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h @@ -100,7 +100,7 @@ struct iwl_priv; struct iwl_sensitivity_ranges; struct iwl_trans_ops; -#define DRV_NAME "iwlagn" +#define DRV_NAME "iwlwifi" #define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" #define DRV_AUTHOR "" From 8a3a3c85e44d58f5af0adac74a0b866ba89a1978 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 2 Oct 2011 10:15:52 +0200 Subject: [PATCH 151/155] mac80211: pass vif param to conf_tx() callback tx params should be configured per interface. add ieee80211_vif param to the conf_tx callback, and change all the drivers that use this callback. The following spatch was used: @rule1@ struct ieee80211_ops ops; identifier conf_tx_op; @@ ops.conf_tx = conf_tx_op; @rule2@ identifier rule1.conf_tx_op; identifier hw, queue, params; @@ conf_tx_op ( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) {...} Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 ++- drivers/net/wireless/ath/carl9170/main.c | 3 ++- drivers/net/wireless/b43/main.c | 3 ++- drivers/net/wireless/b43legacy/main.c | 3 ++- drivers/net/wireless/iwlegacy/iwl-core.c | 3 ++- drivers/net/wireless/iwlegacy/iwl-core.h | 3 ++- drivers/net/wireless/iwlwifi/iwl-core.c | 5 +++-- drivers/net/wireless/iwlwifi/iwl-core.h | 3 ++- drivers/net/wireless/mac80211_hwsim.c | 3 ++- drivers/net/wireless/mwl8k.c | 5 +++-- drivers/net/wireless/p54/main.c | 3 ++- drivers/net/wireless/rt2x00/rt2400pci.c | 5 +++-- drivers/net/wireless/rt2x00/rt2800lib.c | 5 +++-- drivers/net/wireless/rt2x00/rt2800lib.h | 3 ++- drivers/net/wireless/rt2x00/rt2x00.h | 3 ++- drivers/net/wireless/rt2x00/rt2x00mac.c | 3 ++- drivers/net/wireless/rt2x00/rt61pci.c | 5 +++-- drivers/net/wireless/rt2x00/rt73usb.c | 5 +++-- drivers/net/wireless/rtl818x/rtl8187/dev.c | 3 ++- drivers/net/wireless/rtlwifi/core.c | 3 ++- drivers/net/wireless/wl1251/main.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 3 ++- drivers/staging/brcm80211/brcmsmac/mac80211_if.c | 7 ++++--- include/net/mac80211.h | 3 ++- net/mac80211/driver-ops.h | 3 ++- 27 files changed, 61 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index bba4f6fcf7e2..6ed4c0717e3e 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -563,7 +563,7 @@ ath5k_get_stats(struct ieee80211_hw *hw, static int -ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue, +ath5k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath5k_hw *ah = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 17dbbd9d2f53..0b9a0e8a4958 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1352,7 +1352,8 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, return ret; } -static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath9k_htc_priv *priv = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0ebf7321df12..988318665758 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1842,7 +1842,8 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, } } -static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int ath9k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct ath_softc *sc = hw->priv; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 8b780d6d470f..beca71073e9b 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1305,7 +1305,8 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw, return 0; } -static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int carl9170_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct ar9170 *ar = hw->priv; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 43400fb62e1c..7cf4125a1624 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3559,7 +3559,8 @@ static void b43_qos_init(struct b43_wldev *dev) b43dbg(dev->wl, "QoS enabled\n"); } -static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue, +static int b43_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 _queue, const struct ieee80211_tx_queue_params *params) { struct b43_wl *wl = hw_to_b43_wl(hw); diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 468d1836548e..a3b72cd72c66 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2466,7 +2466,8 @@ out: } } -static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { return 0; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c index 8928d47432df..2bd5659310d7 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.c +++ b/drivers/net/wireless/iwlegacy/iwl-core.c @@ -1250,7 +1250,8 @@ void iwl_legacy_clear_isr_stats(struct iwl_priv *priv) memset(&priv->isr_stats, 0, sizeof(priv->isr_stats)); } -int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = hw->priv; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h index b2df01c8f8f5..d1271fe07d4b 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.h +++ b/drivers/net/wireless/iwlegacy/iwl-core.h @@ -286,7 +286,8 @@ struct iwl_cfg { ***************************/ struct ieee80211_hw *iwl_legacy_alloc_all(struct iwl_cfg *cfg); -int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); int iwl_legacy_mac_tx_last_beacon(struct ieee80211_hw *hw); void iwl_legacy_set_rxon_hwcrypto(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index fc400bb2bdff..0725603dbf1d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1123,8 +1123,9 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) &statistics_cmd); } -int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, - const struct ieee80211_tx_queue_params *params) +int iwl_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = hw->priv; struct iwl_rxon_context *ctx; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index e55ffad83950..db50b650756c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -236,7 +236,8 @@ struct iwl_cfg { * L i b * ***************************/ -int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int iwl_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 34b79fc91e39..68455a2307cb 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -970,7 +970,8 @@ static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, } static int mac80211_hwsim_conf_tx( - struct ieee80211_hw *hw, u16 queue, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { wiphy_debug(hw->wiphy, diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ea1395aafa39..995695c28d5c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4915,7 +4915,8 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw, return ret; } -static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int mwl8k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct mwl8k_priv *priv = hw->priv; @@ -5462,7 +5463,7 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) goto fail; for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { - rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]); + rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]); if (rc) goto fail; } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 726a9343f514..ad9ae04d07aa 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -404,7 +404,8 @@ static void p54_configure_filter(struct ieee80211_hw *dev, p54_set_groupfilter(priv); } -static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, +static int p54_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct p54_common *priv = dev->priv; diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 7e9272b8f01d..3a6b40239bc1 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1648,7 +1648,8 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int rt2400pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -1661,7 +1662,7 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, if (queue != 0) return -EINVAL; - if (rt2x00mac_conf_tx(hw, queue, params)) + if (rt2x00mac_conf_tx(hw, vif, queue, params)) return -EINVAL; /* diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9688dd0a7ebd..3f183a15186e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4398,7 +4398,8 @@ int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) } EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); -int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -4414,7 +4415,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 6de128e9c612..8c3c281904fe 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -197,7 +197,8 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev); void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16); int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); -int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index cbf8eb334e96..2ec5c00235e6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1299,7 +1299,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cef1c878c37e..bf0acff07807 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -713,7 +713,8 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 6b6a8e2dcddc..bf55b4a311e3 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2883,7 +2883,8 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +static int rt61pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -2899,7 +2900,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 6f51e39f5595..cfb19dbb0a67 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2222,7 +2222,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, +static int rt73usb_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -2238,7 +2239,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, * we are free to update the registers based on the value * in the queue parameter. */ - retval = rt2x00mac_conf_tx(hw, queue_idx, params); + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); if (retval) return retval; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index f5afa155ce91..24873b55b55c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1241,7 +1241,8 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); } -static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, +static int rtl8187_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rtl8187_priv *priv = dev->priv; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 21e40f62a8d7..3f0f056fae9c 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -504,7 +504,8 @@ static int _rtl_get_hal_qnum(u16 queue) *for mac80211 VO=0, VI=1, BE=2, BK=3 *for rtl819x BE=0, BK=1, VI=2, VO=3 */ -static int rtl_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int rtl_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *param) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index a14a48c99cdc..ba3268ea81fe 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -1158,7 +1158,8 @@ static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 13, .center_freq = 2472}, }; -static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int wl1251_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { enum wl1251_acx_ps_scheme ps_scheme; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7d409b0f3357..e2d6edd2fcd2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3744,7 +3744,8 @@ out: mutex_unlock(&wl->mutex); } -static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int wl1271_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct wl1271 *wl = hw->priv; diff --git a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c index 315dd91800b6..6d71cba3a0a5 100644 --- a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c @@ -141,8 +141,9 @@ static void brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta); -static int brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, - const struct ieee80211_tx_queue_params *params); +static int brcms_ops_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params); static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -556,7 +557,7 @@ brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } static int -brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, +brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct brcms_info *wl = hw->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 135e897b61c7..cd108dfa1952 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2152,7 +2152,8 @@ struct ieee80211_ops { struct ieee80211_sta *sta); void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); - int (*conf_tx)(struct ieee80211_hw *hw, u16 queue, + int (*conf_tx)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 68721d379fe1..5f165d7eb2db 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -423,7 +423,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local, trace_drv_conf_tx(local, sdata, queue, params); if (local->ops->conf_tx) - ret = local->ops->conf_tx(&local->hw, queue, params); + ret = local->ops->conf_tx(&local->hw, &sdata->vif, + queue, params); trace_drv_return_int(local, ret); return ret; } From 8f641d93c38ae93c67263d4e03f793092d471b12 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 3 Oct 2011 11:33:02 +0100 Subject: [PATCH 152/155] libertas: detect TX lockups and reset hardware Recent patches added support for resetting the SD8686 hardware when commands time out, which seems to happen quite frequently soon after resuming the system from a Wake-on-WLAN-triggered resume. At http://dev.laptop.org/ticket/10969 we see the same thing happen with transmits. In this case, the hardware will fail to respond to a frame passed for transmission, and libertas (correctly) will block all further commands and transmissions as the hardware can only deal with one thing at a time. This results in a lockup while the system waits indefinitely for the dead card to respond. Hook up a TX lockup timer to detect this and reset the hardware. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/dev.h | 1 + drivers/net/wireless/libertas/main.c | 35 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index fb3e40bf5902..f3fd447131c2 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -158,6 +158,7 @@ struct lbs_private { /* protected by hard_start_xmit serialization */ u8 txretrycount; struct sk_buff *currenttxskb; + struct timer_list tx_lockup_timer; /* Locks */ struct mutex lock; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index d62d1fb4177f..6a326233391f 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -188,6 +188,7 @@ int lbs_stop_iface(struct lbs_private *priv) spin_unlock_irqrestore(&priv->driver_lock, flags); cancel_work_sync(&priv->mcast_work); + del_timer_sync(&priv->tx_lockup_timer); /* Disable command processing, and wait for all commands to complete */ lbs_deb_main("waiting for commands to complete\n"); @@ -243,6 +244,7 @@ void lbs_host_to_card_done(struct lbs_private *priv) lbs_deb_enter(LBS_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); + del_timer(&priv->tx_lockup_timer); priv->dnld_sent = DNLD_RES_RECEIVED; @@ -585,6 +587,9 @@ static int lbs_thread(void *data) if (ret) { lbs_deb_tx("host_to_card failed %d\n", ret); priv->dnld_sent = DNLD_RES_RECEIVED; + } else { + mod_timer(&priv->tx_lockup_timer, + jiffies + (HZ * 5)); } priv->tx_pending_len = 0; if (!priv->currenttxskb) { @@ -601,6 +606,7 @@ static int lbs_thread(void *data) } del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_THREAD); @@ -734,6 +740,32 @@ out: lbs_deb_leave(LBS_DEB_CMD); } +/** + * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames + * to the hardware. This is known to frequently happen with SD8686 when + * waking up after a Wake-on-WLAN-triggered resume. + * + * @data: &struct lbs_private pointer + */ +static void lbs_tx_lockup_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_TX); + spin_lock_irqsave(&priv->driver_lock, flags); + + netdev_info(priv->dev, "TX lockup detected\n"); + if (priv->reset_card) + priv->reset_card(priv); + + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_TX); +} + /** * auto_deepsleep_timer_fn - put the device back to deep sleep mode when * timer expires and no activity (command, event, data etc.) is detected. @@ -820,6 +852,8 @@ static int lbs_init_adapter(struct lbs_private *priv) setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, (unsigned long)priv); + setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, + (unsigned long)priv); setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, (unsigned long)priv); @@ -857,6 +891,7 @@ static void lbs_free_adapter(struct lbs_private *priv) lbs_free_cmd_buffer(priv); kfifo_free(&priv->event_fifo); del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_MAIN); From 6321eb0977b011ac61dfca36e7c69b2c4325b104 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 30 Sep 2011 11:31:27 +0530 Subject: [PATCH 153/155] ath9k_hw: Fix number of GPIO pins for AR9287/9300 this patch fixes the assumption of maximum number of GPIO pins present in AR9287/AR9300. this fix is essential as we might encounter some functionality issues involved in accessing the status of GPIO pins which are all incorrectly assumed to be not within the range of max_num_gpio of AR9300/AR9287 chipsets Cc: stable@kernel.org Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index f2de7ee047ce..e2c62ea50dad 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2153,6 +2153,10 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->num_gpio_pins = AR9271_NUM_GPIO; else if (AR_DEVID_7010(ah)) pCap->num_gpio_pins = AR7010_NUM_GPIO; + else if (AR_SREV_9300_20_OR_LATER(ah)) + pCap->num_gpio_pins = AR9300_NUM_GPIO; + else if (AR_SREV_9287_11_OR_LATER(ah)) + pCap->num_gpio_pins = AR9287_NUM_GPIO; else if (AR_SREV_9285_12_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; else if (AR_SREV_9280_20_OR_LATER(ah)) From 76ed94be65c8bd80b565865c186dd9f24bb2f23b Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 30 Sep 2011 11:31:28 +0530 Subject: [PATCH 154/155] ath9k_hw: set pci_express capability true for AR9480 the AR_SREV register does not seems to indicate whether AR9480 is pci_express capable or not though the other information like macVersion etc can be obtained properly. this fix is essential as ASPM won't be intialized and its related driver functionality ath9k_hw_configpcipowersave won't be called Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index e2c62ea50dad..42ebe8fb053a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -284,7 +284,12 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) ah->hw_version.macVersion = (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); - ah->is_pciexpress = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; + + if (AR_SREV_9480(ah)) + ah->is_pciexpress = true; + else + ah->is_pciexpress = (val & + AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; } else { if (!AR_SREV_9100(ah)) ah->hw_version.macVersion = MS(val, AR_SREV_VERSION); From 5f68a2b0a890d086e40fc7b55f4a0c32c28bc0d2 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 11 Oct 2011 15:33:10 -0400 Subject: [PATCH 155/155] ath6kl: fixup merge damage in ath6kl_mgmt_tx CC [M] drivers/net/wireless/ath/ath6kl/cfg80211.o drivers/net/wireless/ath/ath6kl/cfg80211.c:1838:2: warning: initialization from incompatible pointer type Caused by commit e9f935e3e8dc0bddd0df6d148165d95925422502... Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 8d9fbd4a62b7..3aff36bad5d3 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1732,7 +1732,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, u64 *cookie) { struct ath6kl *ar = ath6kl_priv(dev); u32 id;