From 138a53ef49ac59860aa6ed0c9ea117d221174a9a Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 23 Apr 2012 19:55:48 -0700 Subject: [PATCH 01/41] libertas: include sched.h on firmware.c Do not assume we have our subsystem including this for us, at least for older kernels this is not true. Lets just be explicit about this requirement for the usage of wake_up(). Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/firmware.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c index 77f6504fd55d..601f2075355e 100644 --- a/drivers/net/wireless/libertas/firmware.c +++ b/drivers/net/wireless/libertas/firmware.c @@ -2,6 +2,7 @@ * Firmware loading and handling functions. */ +#include #include #include #include From a70453b5d04b628e7c9e2fd1db106a20db507930 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:46:56 -0700 Subject: [PATCH 02/41] iwlwifi: remove unused macros Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 69 ---------------------- 1 file changed, 69 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index e1c2bb802050..0642186a90fa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -186,75 +186,6 @@ done: /* * BT coex */ -/* - * Macros to access the lookup table. - * - * The lookup table has 7 inputs: bt3_prio, bt3_txrx, bt_rf_act, wifi_req, -* wifi_prio, wifi_txrx and wifi_sh_ant_req. - * - * It has three outputs: WLAN_ACTIVE, WLAN_KILL and ANT_SWITCH - * - * The format is that "registers" 8 through 11 contain the WLAN_ACTIVE bits - * one after another in 32-bit registers, and "registers" 0 through 7 contain - * the WLAN_KILL and ANT_SWITCH bits interleaved (in that order). - * - * These macros encode that format. - */ -#define LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, wifi_req, wifi_prio, \ - wifi_txrx, wifi_sh_ant_req) \ - (bt3_prio | (bt3_txrx << 1) | (bt_rf_act << 2) | (wifi_req << 3) | \ - (wifi_prio << 4) | (wifi_txrx << 5) | (wifi_sh_ant_req << 6)) - -#define LUT_PTA_WLAN_ACTIVE_OP(lut, op, val) \ - lut[8 + ((val) >> 5)] op (cpu_to_le32(BIT((val) & 0x1f))) -#define LUT_TEST_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - (!!(LUT_PTA_WLAN_ACTIVE_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, \ - bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \ - wifi_sh_ant_req)))) -#define LUT_SET_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - LUT_PTA_WLAN_ACTIVE_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, \ - bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \ - wifi_sh_ant_req)) -#define LUT_CLEAR_PTA_WLAN_ACTIVE(lut, bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, \ - wifi_sh_ant_req) \ - LUT_PTA_WLAN_ACTIVE_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, \ - bt_rf_act, wifi_req, wifi_prio, wifi_txrx, \ - wifi_sh_ant_req)) - -#define LUT_WLAN_KILL_OP(lut, op, val) \ - lut[(val) >> 4] op (cpu_to_le32(BIT(((val) << 1) & 0x1e))) -#define LUT_TEST_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - (!!(LUT_WLAN_KILL_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req)))) -#define LUT_SET_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - LUT_WLAN_KILL_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req)) -#define LUT_CLEAR_WLAN_KILL(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - LUT_WLAN_KILL_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req)) - -#define LUT_ANT_SWITCH_OP(lut, op, val) \ - lut[(val) >> 4] op (cpu_to_le32(BIT((((val) << 1) & 0x1e) + 1))) -#define LUT_TEST_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - (!!(LUT_ANT_SWITCH_OP(lut, &, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, \ - wifi_sh_ant_req)))) -#define LUT_SET_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - LUT_ANT_SWITCH_OP(lut, |=, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req)) -#define LUT_CLEAR_ANT_SWITCH(lut, bt3_prio, bt3_txrx, bt_rf_act, wifi_req, \ - wifi_prio, wifi_txrx, wifi_sh_ant_req) \ - LUT_ANT_SWITCH_OP(lut, &= ~, LUT_VALUE(bt3_prio, bt3_txrx, bt_rf_act, \ - wifi_req, wifi_prio, wifi_txrx, wifi_sh_ant_req)) - static const __le32 iwlagn_def_3w_lookup[12] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), From 9a00be04e66cc025ab4558d34620615d5c4de5b6 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:47:30 -0700 Subject: [PATCH 03/41] iwlwifi: add BT reduced tx power flag Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-commands.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 296347a8290f..c212e539f4f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1891,7 +1891,7 @@ struct iwl_bt_cmd { #define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) #define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) #define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) -#define IWLAGN_BT_VALID_BT4_TIMES cpu_to_le16(BIT(6)) +#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) #define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) #define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ @@ -1900,7 +1900,7 @@ struct iwl_bt_cmd { IWLAGN_BT_VALID_3W_TIMERS | \ IWLAGN_BT_VALID_KILL_ACK_MASK | \ IWLAGN_BT_VALID_KILL_CTS_MASK | \ - IWLAGN_BT_VALID_BT4_TIMES | \ + IWLAGN_BT_VALID_REDUCED_TX_PWR | \ IWLAGN_BT_VALID_3W_LUT) struct iwl_basic_bt_cmd { From 354ce4a4ca613f31871b919e6a86f7fa4cea2582 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:48:01 -0700 Subject: [PATCH 04/41] iwlwifi: add checking for the condition to reduce tx power When bluetooth coex is active and certain condition matched, driver need to decide should the tx power been reduce or not. Adding the logic to manage it. Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 44 +++++++++++++++++++--- drivers/net/wireless/iwlwifi/iwl-dev.h | 1 + 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 0642186a90fa..994a7daa852d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -541,9 +541,10 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv, BT_UART_MSG_FRAME7CONNECTABLE_POS); } -static void iwlagn_set_kill_msk(struct iwl_priv *priv, +static bool iwlagn_set_kill_msk(struct iwl_priv *priv, struct iwl_bt_uart_msg *uart_msg) { + bool need_update = false; u8 kill_msk; static const __le32 bt_kill_ack_msg[2] = { IWLAGN_BT_KILL_ACK_MASK_DEFAULT, @@ -560,10 +561,39 @@ static void iwlagn_set_kill_msk(struct iwl_priv *priv, priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; - - /* schedule to send runtime bt_config */ - queue_work(priv->workqueue, &priv->bt_runtime_config); + need_update = true; } + return need_update; +} + +static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + bool need_update = false; + + if (!priv->reduced_txpower && + !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && + (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)) && + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { + /* enabling reduced tx power */ + priv->reduced_txpower = true; + priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } else if (priv->reduced_txpower && + (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || + (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)))) { + /* disable reduced tx power */ + priv->reduced_txpower = false; + priv->bt_valid &= ~IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } + + return need_update; } int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, @@ -611,7 +641,11 @@ int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, } } - iwlagn_set_kill_msk(priv, uart_msg); + /* schedule to send runtime bt_config */ + if (iwlagn_set_kill_msk(priv, uart_msg) || + iwlagn_fill_txpower_mode(priv, uart_msg)) + queue_work(priv->workqueue, &priv->bt_runtime_config); + /* FIXME: based on notification, adjust the prio_boost */ diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index f70219c3b122..70062379d0ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -921,6 +921,7 @@ struct iwl_priv { __le32 kill_ack_mask; __le32 kill_cts_mask; __le16 bt_valid; + bool reduced_txpower; u16 bt_on_thresh; u16 bt_duration; u16 dynamic_frag_thresh; From 4aa79d91f02b531e50d439291e12cbd8ab31dd66 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:48:34 -0700 Subject: [PATCH 05/41] iwlwifi: add reduced tx power threshold define Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-commands.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index c212e539f4f7..5e02b4fcac0f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -3634,6 +3634,9 @@ enum iwl_bt_coex_profile_traffic_load { (0x3< Date: Thu, 19 Apr 2012 09:49:03 -0700 Subject: [PATCH 06/41] iwlwifi: small define change Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-commands.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 5e02b4fcac0f..15e9bfb52856 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1903,6 +1903,8 @@ struct iwl_bt_cmd { IWLAGN_BT_VALID_REDUCED_TX_PWR | \ IWLAGN_BT_VALID_3W_LUT) +#define IWLAGN_BT_DECISION_LUT_SIZE 12 + struct iwl_basic_bt_cmd { u8 flags; u8 ledtime; /* unused */ @@ -1913,7 +1915,7 @@ struct iwl_basic_bt_cmd { u8 bt3_prio_sample_time; u8 bt3_timer_t2_value; __le16 bt4_reaction_time; /* unused */ - __le32 bt3_lookup_table[12]; + __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; __le16 bt4_decision_time; /* unused */ __le16 valid; }; From 310fbfd9853e7e63a1a84a2be9fe2d9f4a7b7a4e Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:49:44 -0700 Subject: [PATCH 07/41] iwlwifi: send reduce tx power info in command Add the reduce tx power information in bt coex host command Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 1 + drivers/net/wireless/iwlwifi/iwl-commands.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 994a7daa852d..ac483eb2c69d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -256,6 +256,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) basic.kill_ack_mask = priv->kill_ack_mask; basic.kill_cts_mask = priv->kill_cts_mask; + basic.reduce_txpower = priv->reduced_txpower; basic.valid = priv->bt_valid; /* diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 15e9bfb52856..e5dfbfd5fb24 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1916,7 +1916,8 @@ struct iwl_basic_bt_cmd { u8 bt3_timer_t2_value; __le16 bt4_reaction_time; /* unused */ __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; - __le16 bt4_decision_time; /* unused */ + u8 reduce_txpower; + u8 reserved; __le16 valid; }; From 9dc4ca92aa3b1e6808e0296d90857eb4f6fc258b Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:50:06 -0700 Subject: [PATCH 08/41] iwlwifi: change kill mask based on reduce power state In bt coex, consider reduce tx power as part of ack/cts kill mask Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 22 ++++++++++++--------- drivers/net/wireless/iwlwifi/iwl-commands.h | 7 +++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index ac483eb2c69d..9fb4c7d1bf3b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -546,16 +546,19 @@ static bool iwlagn_set_kill_msk(struct iwl_priv *priv, struct iwl_bt_uart_msg *uart_msg) { bool need_update = false; - u8 kill_msk; - static const __le32 bt_kill_ack_msg[2] = { + u8 kill_msk = IWL_BT_KILL_REDUCE; + static const __le32 bt_kill_ack_msg[3] = { IWLAGN_BT_KILL_ACK_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO }; - static const __le32 bt_kill_cts_msg[2] = { + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; + static const __le32 bt_kill_cts_msg[3] = { IWLAGN_BT_KILL_CTS_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO }; + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; - kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) - ? 1 : 0; + if (!priv->reduced_txpower) + kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) + ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; @@ -643,8 +646,9 @@ int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, } /* schedule to send runtime bt_config */ - if (iwlagn_set_kill_msk(priv, uart_msg) || - iwlagn_fill_txpower_mode(priv, uart_msg)) + /* check reduce power before change ack/cts kill mask */ + if (iwlagn_fill_txpower_mode(priv, uart_msg) || + iwlagn_set_kill_msk(priv, uart_msg)) queue_work(priv->workqueue, &priv->bt_runtime_config); diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index e5dfbfd5fb24..83a6930f3658 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1877,9 +1877,16 @@ struct iwl_bt_cmd { #define IWLAGN_BT3_T7_DEFAULT 1 +enum iwl_bt_kill_idx { + IWL_BT_KILL_DEFAULT = 0, + IWL_BT_KILL_OVERRIDE = 1, + IWL_BT_KILL_REDUCE = 2, +}; + #define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) #define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) #define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) +#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) #define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 From 4d5ba61ca3607dacd84a86c61bae7752e3dd9cae Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Thu, 19 Apr 2012 09:50:42 -0700 Subject: [PATCH 09/41] iwlwifi: add loose coex lut Add the Loose coex LUT and will use later for better bt coex tpt Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 9fb4c7d1bf3b..01dc44267317 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -186,7 +186,8 @@ done: /* * BT coex */ -static const __le32 iwlagn_def_3w_lookup[12] = { +/* Notmal TDM */ +static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaeaaaaaa), @@ -201,7 +202,25 @@ static const __le32 iwlagn_def_3w_lookup[12] = { cpu_to_le32(0xf0005000), }; -static const __le32 iwlagn_concurrent_lookup[12] = { + +/* Loose Coex */ +static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + +/* Full concurrency */ +static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), cpu_to_le32(0xaaaaaaaa), From c15797e6af9a26686a93d79e5bb6ce8280379e92 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 19 Apr 2012 10:29:58 +0300 Subject: [PATCH 10/41] iwlwifi: use IWL_* instead of dev_printk when possible Also remove a debug print when allocation error occurred. The kernel will complain anyway. Signed-off-by: Emmanuel Grumbach Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn.c | 11 +++++------ drivers/net/wireless/iwlwifi/iwl-drv.c | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 8bd9610a5d68..ee47b3fcf891 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1419,32 +1419,31 @@ void iwl_set_hw_params(struct iwl_priv *priv) void iwl_debug_config(struct iwl_priv *priv) { - dev_printk(KERN_INFO, priv->trans->dev, "CONFIG_IWLWIFI_DEBUG " + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG " #ifdef CONFIG_IWLWIFI_DEBUG "enabled\n"); #else "disabled\n"); #endif - dev_printk(KERN_INFO, priv->trans->dev, "CONFIG_IWLWIFI_DEBUGFS " + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS " #ifdef CONFIG_IWLWIFI_DEBUGFS "enabled\n"); #else "disabled\n"); #endif - dev_printk(KERN_INFO, priv->trans->dev, "CONFIG_IWLWIFI_DEVICE_TRACING " + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING " #ifdef CONFIG_IWLWIFI_DEVICE_TRACING "enabled\n"); #else "disabled\n"); #endif - - dev_printk(KERN_INFO, priv->trans->dev, "CONFIG_IWLWIFI_DEVICE_TESTMODE " + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE " #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE "enabled\n"); #else "disabled\n"); #endif - dev_printk(KERN_INFO, priv->trans->dev, "CONFIG_IWLWIFI_P2P " + IWL_INFO(priv, "CONFIG_IWLWIFI_P2P " #ifdef CONFIG_IWLWIFI_P2P "enabled\n"); #else diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 05302d6f989f..430cca56a7e7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -66,6 +66,7 @@ #include #include "iwl-drv.h" +#include "iwl-debug.h" #include "iwl-trans.h" #include "iwl-op-mode.h" #include "iwl-agn-hw.h" @@ -892,10 +893,9 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, int ret; drv = kzalloc(sizeof(*drv), GFP_KERNEL); - if (!drv) { - dev_printk(KERN_ERR, trans->dev, "Couldn't allocate iwl_drv"); + if (!drv) return NULL; - } + drv->trans = trans; drv->dev = trans->dev; drv->cfg = cfg; @@ -905,7 +905,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, ret = iwl_request_firmware(drv, true); if (ret) { - dev_printk(KERN_ERR, trans->dev, "Couldn't request the fw"); + IWL_ERR(trans, "Couldn't request the fw\n"); kfree(drv); drv = NULL; } From f6b1154678ec73f7dd9a9fab16b402966305d452 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 19 Apr 2012 09:51:24 -0700 Subject: [PATCH 11/41] iwlwifi: add option to disable 5GHz band There are various problems happened on 5GHz band not observed on 2.4 GHz (microcode errors, queue stuck, etc... ) . Also roaming between 5GHz AP and 2GHz does not work very well. To workaround the problems add option to disable 5GHz support. This will help on environments where APs are dual-band, and devices will not try to associate on band where issues happen. Signed-off-by: Stanislaw Gruszka Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn.c | 3 +++ drivers/net/wireless/iwlwifi/iwl-drv.c | 4 ++++ drivers/net/wireless/iwlwifi/iwl-modparams.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index ee47b3fcf891..00a8f954200c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1307,6 +1307,9 @@ static int iwl_init_geos(struct iwl_priv *priv) priv->hw_params.sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; } + if (iwlwifi_mod_params.disable_5ghz) + priv->bands[IEEE80211_BAND_5GHZ].n_channels = 0; + IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", priv->bands[IEEE80211_BAND_2GHZ].n_channels, priv->bands[IEEE80211_BAND_5GHZ].n_channels); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 430cca56a7e7..3c72bad0ae56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1012,3 +1012,7 @@ module_param_named(auto_agg, iwlwifi_mod_params.auto_agg, bool, S_IRUGO); MODULE_PARM_DESC(auto_agg, "enable agg w/o check traffic load (default: enable)"); + +module_param_named(5ghz_disable, iwlwifi_mod_params.disable_5ghz, + bool, S_IRUGO); +MODULE_PARM_DESC(5ghz_disable, "disable 5GHz band (default: 0 [enabled])"); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index 558b2e63c5cf..ca15d9af2ed2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -103,6 +103,7 @@ enum iwl_power_level { * @ant_coupling: antenna coupling in dB, default = 0 * @bt_ch_announce: BT channel inhibition, default = enable * @auto_agg: enable agg. without check, default = true + * @disable_5ghz: disable 5GHz capability, default = false */ struct iwl_mod_params { int sw_crypto; @@ -119,6 +120,7 @@ struct iwl_mod_params { int ant_coupling; bool bt_ch_announce; bool auto_agg; + bool disable_5ghz; }; #endif /* #__iwl_shared_h__ */ From 8b5bed90a508c93835473fd9d5e9a1d60140aac5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 23 Apr 2012 15:03:06 -0700 Subject: [PATCH 12/41] iwlwifi: don't init trans->reg_lock from the op_mode This doesn't make any sense. Init it from the transport instead. Signed-off-by: Emmanuel Grumbach Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn.c | 1 - drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 00a8f954200c..5f158646479f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1588,7 +1588,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, /* these spin locks will be used in apm_ops.init and EEPROM access * we should init now */ - spin_lock_init(&priv->trans->reg_lock); spin_lock_init(&priv->statistics.lock); /*********************** diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 63557bb85cda..2e57161854b9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -2175,6 +2175,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, /* Initialize the wait queue for commands */ init_waitqueue_head(&trans->wait_command_queue); + spin_lock_init(&trans->reg_lock); return trans; From 62c50543cec42592c67d3c9cef5d426aee7c071f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 21 Apr 2012 15:23:44 +0300 Subject: [PATCH 13/41] wireless: at76c50x: allocating too much data This is a cut and paste mistake, sizeof(struct mib_local) was intended instead of sizeof(struct mib_phy). The call to at76_get_mib() uses sizeof(struct mib_local) correctly, although I changed that to sizeof(*m) for style reasons after discussion with some of the wireless maintainers. The current code works fine because mib_phy structs are larger than mib_local structs. But we may as well clean it up. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index faa8bcb4aac1..3036c0bab056 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1122,12 +1122,12 @@ exit: static void at76_dump_mib_local(struct at76_priv *priv) { int ret; - struct mib_local *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); if (!m) return; - ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local)); + ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); if (ret < 0) { wiphy_err(priv->hw->wiphy, "at76_get_mib (LOCAL) failed: %d\n", ret); From 98e6b9df53e3e561ee75b7ed76dccb09964ba952 Mon Sep 17 00:00:00 2001 From: WarheadsSE Date: Tue, 24 Apr 2012 15:57:21 -0400 Subject: [PATCH 14/41] mwifiex: add support for SD8786 sdio modified: drivers/net/wireless/mwifiex/Kconfig - notate additional chipset modified: drivers/net/wireless/mwifiex/sdio.c - add definition of id (0x9116) - add to switch for firmware load - add MODULE_FIRMWARE modified: drivers/net/wireless/mwifiex/sdio.h - add definition of default firmware name Signed-off-by: Jason Plum Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/Kconfig | 4 ++-- drivers/net/wireless/mwifiex/sdio.c | 7 +++++++ drivers/net/wireless/mwifiex/sdio.h | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index 7f91cc694787..8e384fae3e68 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -10,12 +10,12 @@ config MWIFIEX mwifiex. config MWIFIEX_SDIO - tristate "Marvell WiFi-Ex Driver for SD8787/SD8797" + tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797" depends on MWIFIEX && MMC select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell - 8787/8797 chipsets with SDIO interface. + 8786/8787/8797 chipsets with SDIO interface. If you choose to build it as a module, it will be called mwifiex_sdio. diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 1aa45c4295bb..e0377473282f 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -253,6 +253,8 @@ static int mwifiex_sdio_resume(struct device *dev) return 0; } +/* Device ID for SD8786 */ +#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) /* Device ID for SD8787 */ #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) /* Device ID for SD8797 */ @@ -260,6 +262,7 @@ static int mwifiex_sdio_resume(struct device *dev) /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)}, {}, @@ -1599,6 +1602,9 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->dev = &func->dev; switch (func->device) { + case SDIO_DEVICE_ID_MARVELL_8786: + strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME); + break; case SDIO_DEVICE_ID_MARVELL_8797: strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME); break; @@ -1807,5 +1813,6 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); MODULE_VERSION(SDIO_VERSION); MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 0ead152e3d1e..21033738ef0c 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -28,6 +28,7 @@ #include "main.h" +#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" #define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" From 91a0099c190107821292d3114c396ac858ad723e Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 25 Apr 2012 20:24:24 +0530 Subject: [PATCH 15/41] mac80211: fix rate control update on 2040 bss change The rate control updation never be called on 2040 BSS change. The station should update its rate control on receiving beacon with different HT mode in the HT operation IE. Not doing so, leads to sending frames with higher(ht40) rates whereas AP is operating in lower mode (ht20). Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 03f93f958fa4..dbd4bd92c018 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -210,7 +210,7 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, disable_40 = true; if (sta && (!reconfig || - (disable_40 != !!(sta->sta.ht_cap.cap & + (disable_40 != !(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) { if (disable_40) From 2e4c14a5582addd655e53277861b7ad853bfd6c3 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 26 Apr 2012 13:02:57 -0700 Subject: [PATCH 16/41] mwifiex: fix static checker warnings "oui_type" in structure "ieee_types_vendor_header" is not used separately, so include it in "oui" array. Now complete oui will be compared fixing following warnings. drivers/net/wireless/mwifiex/sta_ioctl.c:1410 mwifiex_set_gen_ie_helper() error: memcmp() 'pvendor_ie->oui' too small (3 vs 4) drivers/net/wireless/mwifiex/sta_ioctl.c:1435 mwifiex_set_gen_ie_helper() error: memcmp() 'pvendor_ie->oui' too small (3 vs 4) drivers/net/wireless/mwifiex/scan.c:1177 mwifiex_update_bss_desc_with_ie() error: memcmp() 'vendor_ie->vend_hdr.oui' too small (3 vs 4) drivers/net/wireless/mwifiex/scan.c:1185 mwifiex_update_bss_desc_with_ie() error: memcmp() 'vendor_ie->vend_hdr.oui' too small (3 vs 4) Reported-by: Dan Carpenter Signed-off-by: Amitkumar Karwar Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 2a2cabadb9b5..5f6adeb9b950 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -993,8 +993,7 @@ struct mwifiex_ie_types_wmm_queue_status { struct ieee_types_vendor_header { u8 element_id; u8 len; - u8 oui[3]; - u8 oui_type; + u8 oui[4]; /* 0~2: oui, 3: oui_type */ u8 oui_subtype; u8 version; } __packed; From e87278e730b11e9852fe0fe967908ef5a4e6e6a0 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 26 Apr 2012 15:01:06 -0700 Subject: [PATCH 17/41] mac80211: insert mesh peer after init Drivers need the station rate info when inserting a new sta_info. The patch "mac80211: refactor mesh peer initialization" wrongly assumed the rate info could be applied after insertion. After further review, this is clearly not the case. This fixes a regression where HT parameters were not applied before inserting the sta_info, causing performance degradation. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- net/mac80211/mesh_plink.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 1ff2a5c63e43..f4124d7c556c 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -102,9 +102,6 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, set_sta_flag(sta, WLAN_STA_WME); - if (sta_info_insert(sta)) - return NULL; - return sta; } @@ -281,6 +278,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; struct sta_info *sta; + bool insert = false; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); @@ -290,6 +288,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, sta = mesh_plink_alloc(sdata, addr); if (!sta) return NULL; + insert = true; } spin_lock_bh(&sta->lock); @@ -306,6 +305,9 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); + if (insert && sta_info_insert(sta)) + return NULL; + return sta; } From c7d258288f2ad11eaa9656f6f9f48272a102fd1b Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 26 Apr 2012 15:01:07 -0700 Subject: [PATCH 18/41] mac80211: don't transmit 40MHz frames to 20MHz peer If a mesh peer indicates it is operating as 20MHz-only in its HT operation IE, have the rate control algorithm respect this by disabling the equivalent bit in the ieee80211_sta HT capabilities. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- net/mac80211/mesh_plink.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f4124d7c556c..6209327840f7 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -302,6 +302,12 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, else memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); + if (elems->ht_operation) + if (!(elems->ht_operation->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) + sta->sta.ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); From 07d3c15acc673ec559bb59681738ba212914d647 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 27 Apr 2012 16:18:47 -0700 Subject: [PATCH 19/41] iwlwifi: modify #ifdef to avoid sparse complain Signed-off-by: Wey-Yi Guy --- drivers/net/wireless/iwlwifi/iwl-agn.c | 35 +++++++++++++------------- drivers/net/wireless/iwlwifi/iwl-agn.h | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 5f158646479f..8d7637083fcf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1420,37 +1420,37 @@ void iwl_set_hw_params(struct iwl_priv *priv) -void iwl_debug_config(struct iwl_priv *priv) +/* show what optional capabilities we have */ +void iwl_option_config(struct iwl_priv *priv) { - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG " #ifdef CONFIG_IWLWIFI_DEBUG - "enabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); #else - "disabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); #endif - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS " + #ifdef CONFIG_IWLWIFI_DEBUGFS - "enabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); #else - "disabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); #endif - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING " + #ifdef CONFIG_IWLWIFI_DEVICE_TRACING - "enabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); #else - "disabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); #endif - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE " + #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - "enabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n"); #else - "disabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n"); #endif - IWL_INFO(priv, "CONFIG_IWLWIFI_P2P " + #ifdef CONFIG_IWLWIFI_P2P - "enabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); #else - "disabled\n"); + IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n"); #endif } @@ -1569,8 +1569,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, SET_IEEE80211_DEV(priv->hw, priv->trans->dev); - /* show what debugging capabilities we have */ - iwl_debug_config(priv); + iwl_option_config(priv); IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 34900e6523dd..79c0fe06f4db 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -523,7 +523,7 @@ static inline const char *iwl_dvm_get_cmd_string(u8 cmd) void iwl_setup_deferred_work(struct iwl_priv *priv); int iwl_send_wimax_coex(struct iwl_priv *priv); int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); -void iwl_debug_config(struct iwl_priv *priv); +void iwl_option_config(struct iwl_priv *priv); void iwl_set_hw_params(struct iwl_priv *priv); void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags); int iwl_init_drv(struct iwl_priv *priv); From 4fb2f7f3b78b60351f3888a32995634e14358da9 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Tue, 24 Apr 2012 07:42:21 -0700 Subject: [PATCH 20/41] iwlwifi: remove the iwl_shared reference Change-Id: I10e42e0cc7dd91047f093ea2c5a55d65c004ada6 Signed-off-by: Wey-Yi Guy Reviewed-on: http://git-mwg.jer.intel.com/gerrit/1939 Tested-by: Jenkins Reviewed-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-modparams.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index ca15d9af2ed2..d9a86d6b2bd7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -60,8 +60,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ -#ifndef __iwl_shared_h__ -#define __iwl_shared_h__ +#ifndef __iwl_modparams_h__ +#define __iwl_modparams_h__ #include #include @@ -123,4 +123,4 @@ struct iwl_mod_params { bool disable_5ghz; }; -#endif /* #__iwl_shared_h__ */ +#endif /* #__iwl_modparams_h__ */ From 7ed85b65ab611ba841d508ae4ff8c45fa0c48c80 Mon Sep 17 00:00:00 2001 From: Stanislav Yakovlev Date: Mon, 16 Apr 2012 06:38:30 -0400 Subject: [PATCH 21/41] net/wireless: ipw2200: Fix WARN_ON occurring in wiphy_register called by ipw_pci_probe The problem was found by Stefan Lippers-Hollmann http://marc.info/?l=linux-wireless&m=132720334512946&w=2 WARNING: at /tmp/buildd/linux-aptosid-3.2/debian/build/source_i386_none/net/wireless/core.c:562 wiphy_register+0x45/0x38d [cfg80211]() Hardware name: TravelMate 290 \xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff Modules linked in: ipw2200(+) iTCO_wdt libipw joydev drm snd_seq snd_timer snd_seq_device iTCO_vendor_support yenta_socket snd intel_agp i2c_i801 pcmcia_rsrc cfg80211 soundcore parport_pc psmouse parport rng_core snd_page_alloc serio_raw pcspkr i2c_algo_bit intel_gtt pcmcia_core evdev irda crc_ccitt rfkill lib80211 processor container ac battery shpchp pci_hotplug button ext4 mbcache jbd2 crc16 dm_mod sd_mod sr_mod crc_t10dif cdrom ata_generic pata_acpi ata_piix libata scsi_mod firewire_ohci firewire_core crc_itu_t 8139too 8139cp mii uhci_hcd ehci_hcd usbcore usb_common [last unloaded: scsi_wait_scan] Pid: 328, comm: modprobe Not tainted 3.2-1.slh.4-aptosid-686 #1 Call Trace: [] ? warn_slowpath_common+0x7c/0x8f [] ? wiphy_register+0x45/0x38d [cfg80211] [] ? wiphy_register+0x45/0x38d [cfg80211] [] ? warn_slowpath_null+0x1b/0x1f [] ? wiphy_register+0x45/0x38d [cfg80211] [] ? internal_create_group+0xf5/0xff [] ? ipw_pci_probe+0xa9a/0xbd0 [ipw2200] [] ? arch_local_irq_save+0xf/0x14 [] ? pci_device_probe+0x53/0x9a [] ? driver_probe_device+0x94/0x124 [] ? pci_match_id+0x15/0x34 [] ? __driver_attach+0x40/0x5b [] ? bus_for_each_dev+0x37/0x60 [] ? driver_attach+0x17/0x1a [] ? driver_probe_device+0x124/0x124 [] ? bus_add_driver+0x92/0x1d1 [] ? 0xe099cfff [] ? driver_register+0x7d/0xd4 [] ? jump_label_module_notify+0xec/0x167 [] ? 0xe099cfff [] ? __pci_register_driver+0x32/0x87 [] ? 0xe099cfff [] ? ipw_init+0x2e/0x72 [ipw2200] [] ? do_one_initcall+0x7d/0x132 [] ? __blocking_notifier_call_chain+0x47/0x4f [] ? sys_init_module+0x13a4/0x159c [] ? sysenter_do_call+0x12/0x28 This warning appears only if we apply Ben Hutchings' fix http://marc.info/?l=linux-wireless&m=132720195012653&w=2 for the bug reported by Cesare Leonardi http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=656813 with cfg80211 warning during device registration ("cfg80211: failed to add phy80211 symlink to netdev!"). We separate device bring up and registration with network stack to avoid the problem. After that Ben Hutchings' fix can be applied to fix the bug. Cc: stable@kernel.org Signed-off-by: Stanislav Yakovlev Tested-by: Stefan Lippers-Hollmann Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/ipw2200.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index b3707dadad15..9a6d99048c17 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -11443,20 +11443,6 @@ static void ipw_bg_down(struct work_struct *work) mutex_unlock(&priv->mutex); } -/* Called by register_netdev() */ -static int ipw_net_init(struct net_device *dev) -{ - int rc = 0; - struct ipw_priv *priv = libipw_priv(dev); - - mutex_lock(&priv->mutex); - if (ipw_up(priv)) - rc = -EIO; - mutex_unlock(&priv->mutex); - - return rc; -} - static int ipw_wdev_init(struct net_device *dev) { int i, rc = 0; @@ -11725,7 +11711,6 @@ static void ipw_prom_free(struct ipw_priv *priv) #endif static const struct net_device_ops ipw_netdev_ops = { - .ndo_init = ipw_net_init, .ndo_open = ipw_net_open, .ndo_stop = ipw_net_stop, .ndo_set_rx_mode = ipw_net_set_multicast_list, @@ -11848,6 +11833,12 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, goto out_release_irq; } + if (ipw_up(priv)) { + mutex_unlock(&priv->mutex); + err = -EIO; + goto out_remove_sysfs; + } + mutex_unlock(&priv->mutex); err = register_netdev(net_dev); if (err) { From f2efd20facac2de96d33210a982820db960d25d1 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 19 Apr 2012 15:04:52 +0200 Subject: [PATCH 22/41] rt2x00: debugfs support - allow a register to be empty Allow a register to be unspecified, therefore not creating its debugfs file entry. Signed-off-by: Anisse Astier Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00debug.c | 71 ++++++++++++----------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 78787fcc919e..5bb122162b5b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -624,22 +624,21 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); data += sprintf(data, "\n"); data += sprintf(data, "register\tbase\twords\twordsize\n"); - data += sprintf(data, "csr\t%d\t%d\t%d\n", - debug->csr.word_base, - debug->csr.word_count, - debug->csr.word_size); - data += sprintf(data, "eeprom\t%d\t%d\t%d\n", - debug->eeprom.word_base, - debug->eeprom.word_count, - debug->eeprom.word_size); - data += sprintf(data, "bbp\t%d\t%d\t%d\n", - debug->bbp.word_base, - debug->bbp.word_count, - debug->bbp.word_size); - data += sprintf(data, "rf\t%d\t%d\t%d\n", - debug->rf.word_base, - debug->rf.word_count, - debug->rf.word_size); +#define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ +{ \ + if(debug->__name.read) \ + data += sprintf(data, __stringify(__name) \ + "\t%d\t%d\t%d\n", \ + debug->__name.word_base, \ + debug->__name.word_count, \ + debug->__name.word_size); \ +} + RT2X00DEBUGFS_SPRINTF_REGISTER(csr); + RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); + RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); + RT2X00DEBUGFS_SPRINTF_REGISTER(rf); +#undef RT2X00DEBUGFS_SPRINTF_REGISTER + blob->size = strlen(blob->data); return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); @@ -694,25 +693,27 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) if (IS_ERR(intf->register_folder) || !intf->register_folder) goto exit; -#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ -({ \ - (__intf)->__name##_off_entry = \ - debugfs_create_u32(__stringify(__name) "_offset", \ - S_IRUSR | S_IWUSR, \ - (__intf)->register_folder, \ - &(__intf)->offset_##__name); \ - if (IS_ERR((__intf)->__name##_off_entry) \ - || !(__intf)->__name##_off_entry) \ - goto exit; \ - \ - (__intf)->__name##_val_entry = \ - debugfs_create_file(__stringify(__name) "_value", \ - S_IRUSR | S_IWUSR, \ - (__intf)->register_folder, \ - (__intf), &rt2x00debug_fop_##__name);\ - if (IS_ERR((__intf)->__name##_val_entry) \ - || !(__intf)->__name##_val_entry) \ - goto exit; \ +#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ +({ \ + if(debug->__name.read) { \ + (__intf)->__name##_off_entry = \ + debugfs_create_u32(__stringify(__name) "_offset", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + &(__intf)->offset_##__name); \ + if (IS_ERR((__intf)->__name##_off_entry) \ + || !(__intf)->__name##_off_entry) \ + goto exit; \ + \ + (__intf)->__name##_val_entry = \ + debugfs_create_file(__stringify(__name) "_value", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + (__intf), &rt2x00debug_fop_##__name); \ + if (IS_ERR((__intf)->__name##_val_entry) \ + || !(__intf)->__name##_val_entry) \ + goto exit; \ + } \ }) RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr); From f2bd7f16e5ae50a26028b4244f7b9c8bba0c48d2 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 19 Apr 2012 15:53:10 +0200 Subject: [PATCH 23/41] rt2x00: Add debugfs access for rfcsr register RFCSR is only used in rt2800. For other chipsets, the debug struct for rfcsr should be zeroed, which isn't be an issue, since the code can now cope with that. Signed-off-by: Anisse Astier Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800.h | 2 ++ drivers/net/wireless/rt2x00/rt2800lib.c | 7 +++++++ drivers/net/wireless/rt2x00/rt2x00debug.c | 11 ++++++++++- drivers/net/wireless/rt2x00/rt2x00debug.h | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index d91f4f628f65..9348521e0832 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -102,6 +102,8 @@ #define BBP_SIZE 0x00ff #define RF_BASE 0x0004 #define RF_SIZE 0x0010 +#define RFCSR_BASE 0x0000 +#define RFCSR_SIZE 0x0040 /* * Number of TX queues. diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 1cd16b416024..dfc90d34be6d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -836,6 +836,13 @@ const struct rt2x00debug rt2800_rt2x00debug = { .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, + .rfcsr = { + .read = rt2800_rfcsr_read, + .write = rt2800_rfcsr_write, + .word_base = RFCSR_BASE, + .word_size = sizeof(u8), + .word_count = RFCSR_SIZE / sizeof(u8), + }, }; EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 5bb122162b5b..3bb8cafbac59 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -70,6 +70,7 @@ struct rt2x00debug_intf { * - eeprom offset/value files * - bbp offset/value files * - rf offset/value files + * - rfcsr offset/value files * - queue folder * - frame dump file * - queue stats file @@ -89,6 +90,8 @@ struct rt2x00debug_intf { struct dentry *bbp_val_entry; struct dentry *rf_off_entry; struct dentry *rf_val_entry; + struct dentry *rfcsr_off_entry; + struct dentry *rfcsr_val_entry; struct dentry *queue_folder; struct dentry *queue_frame_dump_entry; struct dentry *queue_stats_entry; @@ -131,6 +134,7 @@ struct rt2x00debug_intf { unsigned int offset_eeprom; unsigned int offset_bbp; unsigned int offset_rf; + unsigned int offset_rfcsr; }; void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, @@ -525,6 +529,7 @@ RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16); RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8); RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32); +RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8); static ssize_t rt2x00debug_read_dev_flags(struct file *file, char __user *buf, @@ -614,7 +619,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, const struct rt2x00debug *debug = intf->debug; char *data; - data = kzalloc(8 * MAX_LINE_LENGTH, GFP_KERNEL); + data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return NULL; @@ -637,6 +642,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); RT2X00DEBUGFS_SPRINTF_REGISTER(rf); + RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr); #undef RT2X00DEBUGFS_SPRINTF_REGISTER blob->size = strlen(blob->data); @@ -720,6 +726,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr); #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY @@ -771,6 +778,8 @@ void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) debugfs_remove(intf->queue_stats_entry); debugfs_remove(intf->queue_frame_dump_entry); debugfs_remove(intf->queue_folder); + debugfs_remove(intf->rfcsr_val_entry); + debugfs_remove(intf->rfcsr_off_entry); debugfs_remove(intf->rf_val_entry); debugfs_remove(intf->rf_off_entry); debugfs_remove(intf->bbp_val_entry); diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h index fa11409cb5c6..e11d39bdfef7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.h +++ b/drivers/net/wireless/rt2x00/rt2x00debug.h @@ -65,6 +65,7 @@ struct rt2x00debug { RT2X00DEBUGFS_REGISTER_ENTRY(eeprom, u16); RT2X00DEBUGFS_REGISTER_ENTRY(bbp, u8); RT2X00DEBUGFS_REGISTER_ENTRY(rf, u32); + RT2X00DEBUGFS_REGISTER_ENTRY(rfcsr, u8); }; #endif /* RT2X00DEBUG_H */ From b4050790d0a6bfec76e2186d461123cf9586aa34 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 22 Jan 2012 03:09:35 +0000 Subject: [PATCH 24/41] ipw2200: Fix order of device registration Currently cfg80211 fails to create a "phy80211" symlink in sysfs from the net device to the wiphy device. The latter needs to be registered first. Compile-tested only. Reported-by: Cesare Leonardi Signed-off-by: Ben Hutchings Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/ipw2200.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 9a6d99048c17..0036737fe8e3 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -11840,16 +11840,17 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, } mutex_unlock(&priv->mutex); - err = register_netdev(net_dev); - if (err) { - IPW_ERROR("failed to register network device\n"); - goto out_remove_sysfs; - } err = ipw_wdev_init(net_dev); if (err) { IPW_ERROR("failed to register wireless device\n"); - goto out_unregister_netdev; + goto out_remove_sysfs; + } + + err = register_netdev(net_dev); + if (err) { + IPW_ERROR("failed to register network device\n"); + goto out_unregister_wiphy; } #ifdef CONFIG_IPW2200_PROMISCUOUS @@ -11858,10 +11859,8 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, if (err) { IPW_ERROR("Failed to register promiscuous network " "device (error %d).\n", err); - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->a_band.channels); - kfree(priv->ieee->bg_band.channels); - goto out_unregister_netdev; + unregister_netdev(priv->net_dev); + goto out_unregister_wiphy; } } #endif @@ -11873,8 +11872,10 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, return 0; - out_unregister_netdev: - unregister_netdev(priv->net_dev); + out_unregister_wiphy: + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->a_band.channels); + kfree(priv->ieee->bg_band.channels); out_remove_sysfs: sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); out_release_irq: From e19d8baf8cfcc0b96d7578a22801d16400d877bd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 22 Jan 2012 03:11:12 +0000 Subject: [PATCH 25/41] ipw2100: Fix order of device registration Currently cfg80211 fails to create a "phy80211" symlink in sysfs from the net device to the wiphy device. The latter needs to be registered first. Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/ipw2100.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index c72136c07774..9cfae0c08707 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -1963,10 +1963,8 @@ static int ipw2100_wdev_init(struct net_device *dev) wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); - if (wiphy_register(wdev->wiphy)) { - ipw2100_down(priv); + if (wiphy_register(wdev->wiphy)) return -EIO; - } return 0; } @@ -6331,6 +6329,11 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, printk(KERN_INFO DRV_NAME ": Detected Intel PRO/Wireless 2100 Network Connection\n"); + err = ipw2100_wdev_init(dev); + if (err) + goto fail; + registered = 1; + /* Bring up the interface. Pre 0.46, after we registered the * network device we would call ipw2100_up. This introduced a race * condition with newer hotplug configurations (network was coming @@ -6347,11 +6350,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, "Error calling register_netdev.\n"); goto fail; } - registered = 1; - - err = ipw2100_wdev_init(dev); - if (err) - goto fail; + registered = 2; mutex_lock(&priv->action_mutex); @@ -6390,13 +6389,16 @@ out: fail_unlock: mutex_unlock(&priv->action_mutex); - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->bg_band.channels); fail: if (dev) { - if (registered) + if (registered >= 2) unregister_netdev(dev); + if (registered) { + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->bg_band.channels); + } + ipw2100_hw_stop_adapter(priv); ipw2100_disable_interrupts(priv); From e377a4fc768d7d477e58888bdbddd0103d6238bf Mon Sep 17 00:00:00 2001 From: Meenakshi Venkataraman Date: Sun, 22 Apr 2012 07:55:27 -0700 Subject: [PATCH 26/41] iwlwifi: use correct released ucode version Report correctly the latest released version of the iwlwifi firmware for all iwlwifi-supported devices. Cc: stable@vger.kernel.org #3.3+ Signed-off-by: Meenakshi Venkataraman Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-6000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 381b02cf339c..c7887727f8e5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -138,7 +138,7 @@ static const struct iwl_bt_params iwl6000_bt_params = { #define IWL_DEVICE_6005 \ .fw_name_pre = IWL6005_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ + .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6005, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ From 35e7adaaf6932c5ffb22c6ec521734434c65adc5 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Wed, 25 Apr 2012 08:10:08 -0700 Subject: [PATCH 27/41] iwlwifi: use 6000G2B for 6030 device series "iwlwifi: use correct released ucode version" change the ucode api ok from 6000G2 to 6000G2B, but it shall belong to 6030 device series, not the 6005 device series. Fix it Cc: stable@vger.kernel.org #3.3+ Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-6000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index c7887727f8e5..381b02cf339c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -138,7 +138,7 @@ static const struct iwl_bt_params iwl6000_bt_params = { #define IWL_DEVICE_6005 \ .fw_name_pre = IWL6005_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ + .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_6005, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ From d61992182e41e1beec0507fd7bce4ba1face12d6 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 23 Apr 2012 12:50:29 -0700 Subject: [PATCH 28/41] cfg80211: Add framework to support ethtool stats. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- include/net/cfg80211.h | 17 +++++++++++++++++ net/wireless/ethtool.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 815dc3f37e2b..27f956101bbd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1514,6 +1514,16 @@ struct cfg80211_gtk_rekey_data { * later passes to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. + * + * @get_et_sset_count: Ethtool API to get string-set count. + * See @ethtool_ops.get_sset_count + * + * @get_et_stats: Ethtool API to get a set of u64 stats. + * See @ethtool_ops.get_ethtool_stats + * + * @get_et_strings: Ethtool API to get a set of strings to describe stats + * and perhaps other supported types of ethtool data-sets. + * See @ethtool_ops.get_strings */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1712,6 +1722,13 @@ struct cfg80211_ops { struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy, enum nl80211_channel_type *type); + + int (*get_et_sset_count)(struct wiphy *wiphy, + struct net_device *dev, int sset); + void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev, + struct ethtool_stats *stats, u64 *data); + void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, + u32 sset, u8 *data); }; /* diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index 9bde4d1d3e9b..7eecdf40cf80 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -68,6 +68,32 @@ static int cfg80211_set_ringparam(struct net_device *dev, return -ENOTSUPP; } +static int cfg80211_get_sset_count(struct net_device *dev, int sset) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + if (rdev->ops->get_et_sset_count) + return rdev->ops->get_et_sset_count(wdev->wiphy, dev, sset); + return -EOPNOTSUPP; +} + +static void cfg80211_get_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + if (rdev->ops->get_et_stats) + rdev->ops->get_et_stats(wdev->wiphy, dev, stats, data); +} + +static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + if (rdev->ops->get_et_strings) + rdev->ops->get_et_strings(wdev->wiphy, dev, sset, data); +} + const struct ethtool_ops cfg80211_ethtool_ops = { .get_drvinfo = cfg80211_get_drvinfo, .get_regs_len = cfg80211_get_regs_len, @@ -75,4 +101,7 @@ const struct ethtool_ops cfg80211_ethtool_ops = { .get_link = ethtool_op_get_link, .get_ringparam = cfg80211_get_ringparam, .set_ringparam = cfg80211_set_ringparam, + .get_strings = cfg80211_get_strings, + .get_ethtool_stats = cfg80211_get_stats, + .get_sset_count = cfg80211_get_sset_count, }; From b1ab79255c539ebe740baa89f8a44ab139381e1c Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 23 Apr 2012 12:50:30 -0700 Subject: [PATCH 29/41] mac80211: Support getting sta_info stats via ethtool. This lets ethtool print out stats related to stations connected to the interface. Does not yet get stats from the underlying driver. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 70b2af2315a6..31023ca17575 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -450,6 +450,94 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } +static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { + "rx_packets", "rx_bytes", "wep_weak_iv_count", + "rx_duplicates", "rx_fragments", "rx_dropped", + "tx_packets", "tx_bytes", "tx_fragments", + "tx_filtered", "tx_retry_failed", "tx_retries", + "beacon_loss" +}; +#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) + +static int ieee80211_get_et_sset_count(struct wiphy *wiphy, + struct net_device *dev, + int sset) +{ + if (sset == ETH_SS_STATS) + return STA_STATS_LEN; + + return -EOPNOTSUPP; +} + +static void ieee80211_get_et_stats(struct wiphy *wiphy, + struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + struct ieee80211_local *local = sdata->local; + int i; + + memset(data, 0, sizeof(u64) * STA_STATS_LEN); + +#define ADD_STA_STATS(sta) \ + do { \ + data[i++] += sta->rx_packets; \ + data[i++] += sta->rx_bytes; \ + data[i++] += sta->wep_weak_iv_count; \ + data[i++] += sta->num_duplicates; \ + data[i++] += sta->rx_fragments; \ + data[i++] += sta->rx_dropped; \ + \ + data[i++] += sta->tx_packets; \ + data[i++] += sta->tx_bytes; \ + data[i++] += sta->tx_fragments; \ + data[i++] += sta->tx_filtered_count; \ + data[i++] += sta->tx_retry_failed; \ + data[i++] += sta->tx_retry_count; \ + data[i++] += sta->beacon_loss_count; \ + } while (0) + + /* For Managed stations, find the single station based on BSSID + * and use that. For interface types, iterate through all available + * stations and add stats for any station that is assigned to this + * network device. + */ + + rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); + if (sta && !WARN_ON(sta->sdata->dev != dev)) { + i = 0; + ADD_STA_STATS(sta); + BUG_ON(i != STA_STATS_LEN); + } + } else { + list_for_each_entry_rcu(sta, &local->sta_list, list) { + /* Make sure this station belongs to the proper dev */ + if (sta->sdata->dev != dev) + continue; + + i = 0; + ADD_STA_STATS(sta); + BUG_ON(i != STA_STATS_LEN); + } + } + + rcu_read_unlock(); +} + +static void ieee80211_get_et_strings(struct wiphy *wiphy, + struct net_device *dev, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) { + int sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); + memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats); + } +} static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) @@ -2794,4 +2882,7 @@ struct cfg80211_ops mac80211_config_ops = { #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, #endif + .get_et_sset_count = ieee80211_get_et_sset_count, + .get_et_stats = ieee80211_get_et_stats, + .get_et_strings = ieee80211_get_et_strings, }; From e352114fd62f6d568ca0cb18f589cb8df710cf02 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 23 Apr 2012 12:50:31 -0700 Subject: [PATCH 30/41] mac80211: Framework to get wifi-driver stats via ethtool. This adds hooks to call into the driver to get additional stats for the ethtool API. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- include/net/mac80211.h | 17 +++++++++++++++++ net/mac80211/cfg.c | 21 +++++++++++++++++---- net/mac80211/driver-ops.h | 37 +++++++++++++++++++++++++++++++++++++ net/mac80211/driver-trace.h | 15 +++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index da3658177997..4d6e6c6818d0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2223,6 +2223,14 @@ enum ieee80211_rate_control_changed { * 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. + * + * @get_et_sset_count: Ethtool API to get string-set count. + * + * @get_et_stats: Ethtool API to get a set of u64 stats. + * + * @get_et_strings: Ethtool API to get a set of strings to describe stats + * and perhaps other supported types of ethtool data-sets. + * */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2353,6 +2361,15 @@ struct ieee80211_ops { u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data); + + int (*get_et_sset_count)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset); + void (*get_et_stats)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data); + void (*get_et_strings)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data); }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 31023ca17575..a38b26730652 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -463,10 +463,17 @@ static int ieee80211_get_et_sset_count(struct wiphy *wiphy, struct net_device *dev, int sset) { - if (sset == ETH_SS_STATS) - return STA_STATS_LEN; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int rv = 0; - return -EOPNOTSUPP; + if (sset == ETH_SS_STATS) + rv += STA_STATS_LEN; + + rv += drv_get_et_sset_count(sdata, sset); + + if (rv == 0) + return -EOPNOTSUPP; + return rv; } static void ieee80211_get_et_stats(struct wiphy *wiphy, @@ -527,16 +534,22 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, } rcu_read_unlock(); + + drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); } static void ieee80211_get_et_strings(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data) { + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int sz_sta_stats = 0; + if (sset == ETH_SS_STATS) { - int sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); + sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats); } + drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); } static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4a0e559cb26b..6d33a0c743ab 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -35,6 +35,43 @@ static inline void drv_tx_frags(struct ieee80211_local *local, local->ops->tx_frags(&local->hw, vif, sta, skbs); } +static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, + u32 sset, u8 *data) +{ + struct ieee80211_local *local = sdata->local; + if (local->ops->get_et_strings) { + trace_drv_get_et_strings(local, sset); + local->ops->get_et_strings(&local->hw, &sdata->vif, sset, data); + trace_drv_return_void(local); + } +} + +static inline void drv_get_et_stats(struct ieee80211_sub_if_data *sdata, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_local *local = sdata->local; + if (local->ops->get_et_stats) { + trace_drv_get_et_stats(local); + local->ops->get_et_stats(&local->hw, &sdata->vif, stats, data); + trace_drv_return_void(local); + } +} + +static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata, + int sset) +{ + struct ieee80211_local *local = sdata->local; + int rv = 0; + if (local->ops->get_et_sset_count) { + trace_drv_get_et_sset_count(local, sset); + rv = local->ops->get_et_sset_count(&local->hw, &sdata->vif, + sset); + trace_drv_return_int(local, rv); + } + return rv; +} + static inline int drv_start(struct ieee80211_local *local) { int ret; diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 7c0754bed61b..6de00b2c268c 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -161,6 +161,21 @@ DEFINE_EVENT(local_only_evt, drv_start, TP_ARGS(local) ); +DEFINE_EVENT(local_u32_evt, drv_get_et_strings, + TP_PROTO(struct ieee80211_local *local, u32 sset), + TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_u32_evt, drv_get_et_sset_count, + TP_PROTO(struct ieee80211_local *local, u32 sset), + TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_only_evt, drv_get_et_stats, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + DEFINE_EVENT(local_only_evt, drv_suspend, TP_PROTO(struct ieee80211_local *local), TP_ARGS(local) From 3073a7c20cea0b7a9946fe61f09d43aa61deb9ea Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 23 Apr 2012 12:50:32 -0700 Subject: [PATCH 31/41] mac80211: Add more ethtools stats: survey, rates, etc The signal and noise are forced to be positive since ethtool deals in unsigned 64-bit values and this number should be human readable. This gives easy access to some of the data formerly exposed in the deprecated /proc/net/wireless file. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 89 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a38b26730652..39b1fffb24f4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -455,7 +455,9 @@ static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { "rx_duplicates", "rx_fragments", "rx_dropped", "tx_packets", "tx_bytes", "tx_fragments", "tx_filtered", "tx_retry_failed", "tx_retries", - "beacon_loss" + "beacon_loss", "sta_state", "txrate", "rxrate", "signal", + "channel", "noise", "ch_time", "ch_time_busy", + "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" }; #define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) @@ -484,7 +486,10 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; struct ieee80211_local *local = sdata->local; - int i; + struct station_info sinfo; + struct survey_info survey; + int i, q; +#define STA_STATS_SURVEY_LEN 7 memset(data, 0, sizeof(u64) * STA_STATS_LEN); @@ -516,11 +521,30 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, if (sdata->vif.type == NL80211_IFTYPE_STATION) { sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); - if (sta && !WARN_ON(sta->sdata->dev != dev)) { - i = 0; - ADD_STA_STATS(sta); - BUG_ON(i != STA_STATS_LEN); - } + + if (!(sta && !WARN_ON(sta->sdata->dev != dev))) + goto do_survey; + + i = 0; + ADD_STA_STATS(sta); + + data[i++] = sta->sta_state; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + + if (sinfo.filled | STATION_INFO_TX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.txrate); + i++; + if (sinfo.filled | STATION_INFO_RX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.rxrate); + i++; + + if (sinfo.filled | STATION_INFO_SIGNAL_AVG) + data[i] = (u8)sinfo.signal_avg; + i++; } else { list_for_each_entry_rcu(sta, &local->sta_list, list) { /* Make sure this station belongs to the proper dev */ @@ -529,12 +553,61 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, i = 0; ADD_STA_STATS(sta); - BUG_ON(i != STA_STATS_LEN); } } +do_survey: + i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; + /* Get survey stats for current channel */ + q = 0; + while (true) { + survey.filled = 0; + if (drv_get_survey(local, q, &survey) != 0) { + survey.filled = 0; + break; + } + + if (survey.channel && + (local->oper_channel->center_freq == + survey.channel->center_freq)) + break; + q++; + } + + if (survey.filled) + data[i++] = survey.channel->center_freq; + else + data[i++] = 0; + if (survey.filled & SURVEY_INFO_NOISE_DBM) + data[i++] = (u8)survey.noise; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME) + data[i++] = survey.channel_time; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + data[i++] = survey.channel_time_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + data[i++] = survey.channel_time_ext_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) + data[i++] = survey.channel_time_rx; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) + data[i++] = survey.channel_time_tx; + else + data[i++] = -1LL; + rcu_read_unlock(); + if (WARN_ON(i != STA_STATS_LEN)) + return; + drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); } From f0116fbfcb2559a32e6387ee1699cccf977dab73 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 27 Apr 2012 18:56:56 -0700 Subject: [PATCH 32/41] brcmfmac: stop releasing sdio host in irq handler brcmf_sdbrcm_isr doesn't access to the dongle through SDIO bus. Stop releasing and claiming host in irq handler to eliminate any potential risk. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e925290b432b..ea350200a7db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -45,11 +45,7 @@ static void brcmf_sdioh_irqhandler(struct sdio_func *func) brcmf_dbg(TRACE, "***IRQHandler\n"); - sdio_release_host(func); - brcmf_sdbrcm_isr(sdiodev->bus); - - sdio_claim_host(func); } /* dummy handler for SDIO function 2 interrupt */ From d9126e0c420b1ced6a2482aeda8380bca7fbebb0 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 27 Apr 2012 18:56:57 -0700 Subject: [PATCH 33/41] brcmfmac: check bus state for status Bus state should be the correct flag for bus status. Use it instead of result from previous function call for backplane clock switch. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a80b840051a7..ad46f34b582e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3509,7 +3509,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); /* If we didn't come up, turn off backplane clock */ - if (!ret) + if (bus_if->state != BRCMF_BUS_DATA) brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); exit: From e2f93cc3218853a3c00bd7c9f923bec65aaf9103 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 27 Apr 2012 18:56:58 -0700 Subject: [PATCH 34/41] brcmfmac: postpone interrupt register function For out of band interrupt which is going to be introduced shortly, the interrupt register function must be called after firmware is downloaded. This patch moves it from brcmf_sdbrcm_probe to brcmf_sdbrcm_bus_init. Reviewed-by: pieter-paul giesberts Reviewed-by: arend van spriel Signed-off-by: franky lin Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ad46f34b582e..e3b1c32c83bb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3508,6 +3508,12 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); + if (ret == 0) { + ret = brcmf_sdcard_intr_reg(bus->sdiodev); + if (ret != 0) + brcmf_dbg(ERROR, "intr register failed:%d\n", ret); + } + /* If we didn't come up, turn off backplane clock */ if (bus_if->state != BRCMF_BUS_DATA) brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); @@ -3968,15 +3974,6 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) goto fail; } - /* Register interrupt callback, but mask it (not operational yet). */ - brcmf_dbg(INTR, "disable SDIO interrupts (not interested yet)\n"); - ret = brcmf_sdcard_intr_reg(bus->sdiodev); - if (ret != 0) { - brcmf_dbg(ERROR, "FAILED: sdcard_intr_reg returned %d\n", ret); - goto fail; - } - brcmf_dbg(INTR, "registered SDIO interrupt function ok\n"); - brcmf_dbg(INFO, "completed!!\n"); /* if firmware path present try to download and bring up bus */ From ba89bf1961bb991a5c6415bd8408a8cb61ee46dc Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 27 Apr 2012 18:56:59 -0700 Subject: [PATCH 35/41] brcmfmac: add out of band interrupt support Some sdio host controllers do not support real in band interrupt. Software polling mode as a replacement is not fast enough for high throughput and new features. Also some in band interrupts do not support host wake up on embedded platform even when they are real physical interrupts. Therefore out of band (oob) interrupt mechanism is implemented for these scenarios. To provide oob irq number and flags used for irq registration in brcmfmac, a platform device contains irq resource must be registered in board specific code. Here is an example of platform device structure: struct resource brcmf_sdio_res[] = { { .start = GPIO_BRCMF_SDIO_OOB_NUM, .end = GPIO_BRCMF_SDIO_OOB_NUM, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, } }; struct platform_device brcmf_sdio_device = { .name = "brcmf_sdio_pd", .id = -1, .num_resources = ARRAY_SIZE(brcmf_sdio_res), .resource = brcmf_sdio_res, }; Reviewed-by: pieter-paul giesberts Reviewed-by: arend van spriel Signed-off-by: franky lin Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/Kconfig | 9 ++ .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 95 ++++++++++++++-- .../brcm80211/brcmfmac/bcmsdh_sdmmc.c | 105 +++++++++++++++++- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 24 +++- .../wireless/brcm80211/brcmfmac/sdio_host.h | 22 +++- 5 files changed, 240 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index c5104533e24e..b480088b3dbe 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -36,6 +36,15 @@ config BRCMFMAC_SDIO IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for a SDIO wireless card. +config BRCMFMAC_SDIO_OOB + bool "Out of band interrupt support for SDIO interface chipset" + depends on BRCMFMAC_SDIO + ---help--- + This option enables out-of-band interrupt support for Broadcom + SDIO Wifi chipset using fullmac in order to gain better + performance and deep sleep wake up capability on certain + platforms. Say N if you are unsure. + config BRCMFMAC_USB bool "USB bus interface support for FullMAC driver" depends on USB diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index ea350200a7db..4add7da24681 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -39,33 +39,113 @@ #define SDIOH_API_ACCESS_RETRY_LIMIT 2 -static void brcmf_sdioh_irqhandler(struct sdio_func *func) +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id) +{ + struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(dev_id); + + brcmf_dbg(INTR, "oob intr triggered\n"); + + /* + * out-of-band interrupt is level-triggered which won't + * be cleared until dpc + */ + if (sdiodev->irq_en) { + disable_irq_nosync(irq); + sdiodev->irq_en = false; + } + + brcmf_sdbrcm_isr(sdiodev->bus); + + return IRQ_HANDLED; +} + +int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) +{ + int ret = 0; + u8 data; + unsigned long flags; + + brcmf_dbg(TRACE, "Entering\n"); + + brcmf_dbg(ERROR, "requesting irq %d\n", sdiodev->irq); + ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, + sdiodev->irq_flags, "brcmf_oob_intr", + &sdiodev->func[1]->card->dev); + if (ret != 0) + return ret; + spin_lock_init(&sdiodev->irq_en_lock); + spin_lock_irqsave(&sdiodev->irq_en_lock, flags); + sdiodev->irq_en = true; + spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); + + ret = enable_irq_wake(sdiodev->irq); + if (ret != 0) + return ret; + sdiodev->irq_wake = true; + + /* must configure SDIO_CCCR_IENx to enable irq */ + data = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_0, + SDIO_CCCR_IENx, &ret); + data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_IENx, + data, &ret); + + /* redirect, configure ane enable io for interrupt signal */ + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; + if (sdiodev->irq_flags | IRQF_TRIGGER_HIGH) + data |= SDIO_SEPINT_ACT_HI; + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_BRCM_SEPINT, + data, &ret); + + return 0; +} + +int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) +{ + brcmf_dbg(TRACE, "Entering\n"); + + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_BRCM_SEPINT, + 0, NULL); + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_IENx, 0, NULL); + + if (sdiodev->irq_wake) { + disable_irq_wake(sdiodev->irq); + sdiodev->irq_wake = false; + } + free_irq(sdiodev->irq, &sdiodev->func[1]->card->dev); + sdiodev->irq_en = false; + + return 0; +} +#else /* CONFIG_BRCMFMAC_SDIO_OOB */ +static void brcmf_sdio_irqhandler(struct sdio_func *func) { struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); - brcmf_dbg(TRACE, "***IRQHandler\n"); + brcmf_dbg(INTR, "ib intr triggered\n"); brcmf_sdbrcm_isr(sdiodev->bus); } /* dummy handler for SDIO function 2 interrupt */ -static void brcmf_sdioh_dummy_irq_handler(struct sdio_func *func) +static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) { } -int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev) +int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); sdio_claim_host(sdiodev->func[1]); - sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler); - sdio_claim_irq(sdiodev->func[2], brcmf_sdioh_dummy_irq_handler); + sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler); + sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); sdio_release_host(sdiodev->func[1]); return 0; } -int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev) +int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); @@ -76,6 +156,7 @@ int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev) return 0; } +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr, int *err) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 758c115b556e..dd07d33a927c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -27,6 +27,7 @@ #include #include /* request_irq() */ #include +#include #include #include @@ -55,6 +56,15 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static struct list_head oobirq_lh; +struct brcmf_sdio_oobirq { + unsigned int irq; + unsigned long flags; + struct list_head list; +}; +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ + static bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) { @@ -107,7 +117,8 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, } sdio_release_host(sdfunc); } - } else if (regaddr == SDIO_CCCR_ABORT) { + } else if ((regaddr == SDIO_CCCR_ABORT) || + (regaddr == SDIO_CCCR_IENx)) { sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), GFP_KERNEL); if (!sdfunc) @@ -467,12 +478,40 @@ void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) } +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev) +{ + struct brcmf_sdio_oobirq *oobirq_entry; + + if (list_empty(&oobirq_lh)) { + brcmf_dbg(ERROR, "no valid oob irq resource\n"); + return -ENXIO; + } + + oobirq_entry = list_first_entry(&oobirq_lh, struct brcmf_sdio_oobirq, + list); + + sdiodev->irq = oobirq_entry->irq; + sdiodev->irq_flags = oobirq_entry->flags; + list_del(&oobirq_entry->list); + kfree(oobirq_entry); + + return 0; +} +#else +static inline int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev) +{ + return 0; +} +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ + static int brcmf_ops_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret = 0; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; + brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(TRACE, "func->class=%x\n", func->class); brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor); @@ -511,6 +550,10 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, sdiodev = dev_get_drvdata(&func->card->dev); if ((!sdiodev) || (sdiodev->func[1]->card != func->card)) return -ENODEV; + + ret = brcmf_sdio_getintrcfg(sdiodev); + if (ret) + return ret; sdiodev->func[2] = func; bus_if = sdiodev->bus_if; @@ -603,6 +646,65 @@ static struct sdio_driver brcmf_sdmmc_driver = { #endif /* CONFIG_PM_SLEEP */ }; +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static int brcmf_sdio_pd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct brcmf_sdio_oobirq *oobirq_entry; + int i, ret; + + INIT_LIST_HEAD(&oobirq_lh); + + for (i = 0; ; i++) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) + break; + + oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq), + GFP_KERNEL); + oobirq_entry->irq = res->start; + oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK; + list_add_tail(&oobirq_entry->list, &oobirq_lh); + } + if (i == 0) + return -ENXIO; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + + if (ret) + brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret); + + return ret; +} + +static struct platform_driver brcmf_sdio_pd = { + .probe = brcmf_sdio_pd_probe, + .driver = { + .name = "brcmf_sdio_pd" + } +}; + +void brcmf_sdio_exit(void) +{ + brcmf_dbg(TRACE, "Enter\n"); + + sdio_unregister_driver(&brcmf_sdmmc_driver); + + platform_driver_unregister(&brcmf_sdio_pd); +} + +void brcmf_sdio_init(void) +{ + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + ret = platform_driver_register(&brcmf_sdio_pd); + + if (ret) + brcmf_dbg(ERROR, "platform_driver_register failed: %d\n", ret); +} +#else void brcmf_sdio_exit(void) { brcmf_dbg(TRACE, "Enter\n"); @@ -621,3 +723,4 @@ void brcmf_sdio_init(void) if (ret) brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret); } +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index e3b1c32c83bb..149ee67beb2e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2352,6 +2352,24 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) up(&bus->sdsem); } +#ifdef CONFIG_BRCMFMAC_SDIO_OOB +static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +{ + unsigned long flags; + + spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); + if (!bus->sdiodev->irq_en && !bus->ipend) { + enable_irq(bus->sdiodev->irq); + bus->sdiodev->irq_en = true; + } + spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); +} +#else +static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +{ +} +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ + static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) { u32 intstatus, newstatus = 0; @@ -2509,6 +2527,8 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->intstatus = intstatus; clkwait: + brcmf_sdbrcm_clrintr(bus); + if (data_ok(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; @@ -3509,7 +3529,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); if (ret == 0) { - ret = brcmf_sdcard_intr_reg(bus->sdiodev); + ret = brcmf_sdio_intr_register(bus->sdiodev); if (ret != 0) brcmf_dbg(ERROR, "intr register failed:%d\n", ret); } @@ -3873,7 +3893,7 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) if (bus) { /* De-register interrupt handler */ - brcmf_sdcard_intr_dereg(bus->sdiodev); + brcmf_sdio_intr_unregister(bus->sdiodev); if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 0281d207d998..7010eaf71f99 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -43,6 +43,13 @@ /* as of sdiod rev 0, supports 3 functions */ #define SBSDIO_NUM_FUNCTION 3 +/* function 0 vendor specific CCCR registers */ +#define SDIO_CCCR_BRCM_SEPINT 0xf2 + +#define SDIO_SEPINT_MASK 0x01 +#define SDIO_SEPINT_OE 0x02 +#define SDIO_SEPINT_ACT_HI 0x04 + /* function 1 miscellaneous registers */ /* sprom command and status */ @@ -144,13 +151,18 @@ struct brcmf_sdio_dev { wait_queue_head_t request_buffer_wait; struct device *dev; struct brcmf_bus *bus_if; +#ifdef CONFIG_BRCMFMAC_SDIO_OOB + unsigned int irq; /* oob interrupt number */ + unsigned long irq_flags; /* board specific oob flags */ + bool irq_en; /* irq enable flags */ + spinlock_t irq_en_lock; + bool irq_wake; /* irq wake enable flags */ +#endif /* CONFIG_BRCMFMAC_SDIO_OOB */ }; -/* Register/deregister device interrupt handler. */ -extern int -brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev); - -extern int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev); +/* Register/deregister interrupt handler. */ +extern int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); +extern int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); /* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface). * fn: function number From ef9456a85dabe2222a4cb80fe8eef6361170c55d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 30 Apr 2012 10:23:36 +0300 Subject: [PATCH 36/41] cfg80211: fix BSS comparison Since the BSS table is organized in a RB tree, the BSSs need to be comparable. This means that we must define a < and > operator to the BSS object. compare_ethr_addr isn't enough since it returns only a binary value. Since Felix's cfg80211: use compare_ether_addr on MAC addresses instead of memcmp Because of the constant size and guaranteed 16 bit alignment, the inline compare_ether_addr function is much cheaper than calling memcmp. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville The BSS table is corrupted: rb_find_bss can't find the bss. As a result BSSes are duplicated in the BSS table, and we get stuck while probing an AP before associating (in STA mode). Change-Id: I85928756f4328028230832c1565ece7f412f3843 CC: Felix Fietkau Signed-off-by: Emmanuel Grumbach Acked-by: Felix Fietkau Signed-off-by: John W. Linville --- net/wireless/scan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1442bb68a3f3..9dee87c0358c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -378,7 +378,11 @@ static int cmp_bss_core(struct cfg80211_bss *a, b->len_information_elements); } - return compare_ether_addr(a->bssid, b->bssid); + /* + * we can't use compare_ether_addr here since we need a < > operator. + * The binary return value of compare_ether_addr isn't enough + */ + return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); } static int cmp_bss(struct cfg80211_bss *a, From 431e31542383b71bc5f2642572a1e6ef07f1bb87 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 30 Apr 2012 14:20:29 -0700 Subject: [PATCH 37/41] mac80211: Advertise HT protection mode in IEs Signed-off-by: Ashok Nagarajan Reviewed-by: Thomas Pedersen Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/mesh.c | 3 ++- net/mac80211/util.c | 9 +++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 61cd391c32a3..bb1a3e62a66a 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -164,7 +164,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chan, channel_type); + chan, channel_type, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6cd89d414f22..ae046b52d5e2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1497,7 +1497,8 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type); + enum nl80211_channel_type channel_type, + u16 prot_mode); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 598a96a3a051..8a952e04690a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -396,7 +396,8 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); - ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type); + ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type, + sdata->vif.bss_conf.ht_operation_mode); return 0; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d9a747d387f0..22f2216b397e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1663,7 +1663,8 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) + enum nl80211_channel_type channel_type, + u16 prot_mode) { struct ieee80211_ht_operation *ht_oper; /* Build HT Information */ @@ -1689,11 +1690,7 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, channel_type != NL80211_CHAN_HT20) ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; - /* - * Note: According to 802.11n-2009 9.13.3.1, HT Protection field and - * RIFS Mode are reserved in IBSS mode, therefore keep them at 0 - */ - ht_oper->operation_mode = 0x0000; + ht_oper->operation_mode = cpu_to_le16(prot_mode); ht_oper->stbc_param = 0x0000; /* It seems that Basic MCS set and Supported MCS set From 57aac7c51c07ca7a2361477f352af422259301bd Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 30 Apr 2012 14:20:30 -0700 Subject: [PATCH 38/41] mac80211: Implement HT mixed protection mode Section 9.23.3.5 of IEEE 80211s standard describes the protection rules for HT mesh STA in a MBSS. Three HT protection modes are supported for now: non-HT mixed mode - is selected if any non-HT peers are present in our MBSS. 20MHz-protection mode - is selected if all peers in our 20/40MHz MBSS support HT and atleast one HT20 peer is present. no-protection mode - is selected otherwise. This is a limited implementation of 9.23.3.5, which only considers mesh peers when determining the HT protection mode. Station's channel_type needs to be maintained. Signed-off-by: Ashok Nagarajan Reviewed-by: Thomas Pedersen Signed-off-by: John W. Linville --- net/mac80211/mesh_plink.c | 83 +++++++++++++++++++++++++++++++++++---- net/mac80211/sta_info.h | 1 + 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6209327840f7..2e0ae7310697 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -105,6 +105,66 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, return sta; } +/** mesh_set_ht_prot_mode - set correct HT protection mode + * + * Section 9.23.3.5 of IEEE 80211s standard describes the protection rules for + * HT mesh STA in a MBSS. Three HT protection modes are supported for now, + * non-HT mixed mode, 20MHz-protection and no-protection mode. non-HT mixed + * mode is selected if any non-HT peers are present in our MBSS. + * 20MHz-protection mode is selected if all peers in our 20/40MHz MBSS support + * HT and atleast one HT20 peer is present. Otherwise no-protection mode is + * selected. + */ +static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + u32 changed = 0; + u16 ht_opmode; + bool non_ht_sta = false, ht20_sta = false; + + if (local->_oper_channel_type == NL80211_CHAN_NO_HT) + return 0; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sdata == sta->sdata && + sta->plink_state == NL80211_PLINK_ESTAB) { + switch (sta->ch_type) { + case NL80211_CHAN_NO_HT: + mpl_dbg("mesh_plink %pM: nonHT sta (%pM) is present", + sdata->vif.addr, sta->sta.addr); + non_ht_sta = true; + goto out; + case NL80211_CHAN_HT20: + mpl_dbg("mesh_plink %pM: HT20 sta (%pM) is present", + sdata->vif.addr, sta->sta.addr); + ht20_sta = true; + default: + break; + } + } + } +out: + rcu_read_unlock(); + + if (non_ht_sta) + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; + else if (ht20_sta && local->_oper_channel_type > NL80211_CHAN_HT20) + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; + else + ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { + sdata->vif.bss_conf.ht_operation_mode = ht_opmode; + changed = BSS_CHANGED_HT; + mpl_dbg("mesh_plink %pM: protection mode changed to %d", + sdata->vif.addr, ht_opmode); + } + + return changed; +} + /** * __mesh_plink_deactivate - deactivate mesh peer link * @@ -302,11 +362,14 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, else memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); - if (elems->ht_operation) + if (elems->ht_operation) { if (!(elems->ht_operation->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sta->ch_type = + ieee80211_ht_oper_to_channel_type(elems->ht_operation); + } rate_control_rate_init(sta); spin_unlock_bh(&sta->lock); @@ -495,9 +558,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m enum plink_event event; enum ieee80211_self_protected_actioncode ftype; size_t baselen; - bool deactivated, matches_local = true; + bool matches_local = true; u8 ie_len; u8 *baseaddr; + u32 changed = 0; __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { @@ -783,7 +847,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; @@ -818,7 +883,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, @@ -836,13 +902,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m case CLS_ACPT: reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; - deactivated = __mesh_plink_deactivate(sta); + __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); - if (deactivated) - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + changed |= mesh_set_ht_prot_mode(sdata); + changed |= BSS_CHANGED_BEACON; mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; @@ -889,4 +955,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } rcu_read_unlock(); + + if (changed) + ieee80211_bss_info_change_notify(sdata, changed); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index f75f5d9ac06d..663dc90c4e31 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -362,6 +362,7 @@ struct sta_info { struct timer_list plink_timer; s64 t_offset; s64 t_offset_setpoint; + enum nl80211_channel_type ch_type; #endif #ifdef CONFIG_MAC80211_DEBUGFS From b91e64aad2c78b0477b9eb3a26335668b4032002 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 30 Apr 2012 14:20:31 -0700 Subject: [PATCH 39/41] mac80211: Allow nonHT/HT peering in mesh Now that we have protection enabled, allow non-HT and HT20 stations to peer with HT40+/- stations. Peering is still disallowed for HT40+/- mismatch. Signed-off-by: Ashok Nagarajan Reviewed-by: Thomas Pedersen Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8a952e04690a..0fabb8bae9fb 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -76,6 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; u32 basic_rates = 0; + enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT; /* * As support for each feature is added, check for matching @@ -102,10 +103,15 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, if (sdata->vif.bss_conf.basic_rates != basic_rates) goto mismatch; - /* disallow peering with mismatched channel types for now */ + if (ie->ht_operation) + sta_channel_type = + ieee80211_ht_oper_to_channel_type(ie->ht_operation); + + /* Disallow HT40+/- mismatch */ if (ie->ht_operation && - (local->_oper_channel_type != - ieee80211_ht_oper_to_channel_type(ie->ht_operation))) + local->_oper_channel_type > NL80211_CHAN_HT20 && + sta_channel_type > NL80211_CHAN_HT20 && + local->_oper_channel_type != sta_channel_type) goto mismatch; return true; From 70c33eaae79e53f9e48324736c0cb85534d3f093 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 30 Apr 2012 14:20:32 -0700 Subject: [PATCH 40/41] {nl,cfg,mac}80211: Allow user to see/configure HT protection mode This patch introduces a new mesh configuration parameter "ht_opmode" and will allow user to check the current HT protection mode selected. Users could configure the protection mode by the command "iw mesh_iface set mesh_param mesh_ht_protection_mode=2". The default protection mode of mesh is set to non-HT mixed mode. Signed-off-by: Ashok Nagarajan Reviewed-by: Thomas Pedersen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 3 +++ include/net/cfg80211.h | 1 + net/mac80211/cfg.c | 5 +++++ net/mac80211/mesh.c | 3 +++ net/mac80211/mesh_plink.c | 1 + net/wireless/mesh.c | 1 + net/wireless/nl80211.c | 7 ++++++- 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 1335084b1c69..2540e86d99ab 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2154,6 +2154,8 @@ enum nl80211_mntr_flags { * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors * to synchronize to for 11s default synchronization method (see 11C.12.2.2) * + * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2179,6 +2181,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_FORWARDING, NL80211_MESHCONF_RSSI_THRESHOLD, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + NL80211_MESHCONF_HT_OPMODE, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 27f956101bbd..adb2320bccdf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -821,6 +821,7 @@ struct mesh_config { bool dot11MeshGateAnnouncementProtocol; bool dot11MeshForwarding; s32 rssi_threshold; + u16 ht_opmode; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 39b1fffb24f4..0221270c0ddf 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1538,6 +1538,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, return -ENOTSUPP; conf->rssi_threshold = nconf->rssi_threshold; } + if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) { + conf->ht_opmode = nconf->ht_opmode; + sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); + } return 0; } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0fabb8bae9fb..0a21e4e55f43 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -595,12 +595,15 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); ieee80211_mesh_root_setup(ifmsh); ieee80211_queue_work(&local->hw, &sdata->work); + sdata->vif.bss_conf.ht_operation_mode = + ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sdata->local, sdata->local->hw.conf.channel->band); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 2e0ae7310697..8cc8461b48a0 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -157,6 +157,7 @@ out: if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { sdata->vif.bss_conf.ht_operation_mode = ht_opmode; + sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; changed = BSS_CHANGED_HT; mpl_dbg("mesh_plink %pM: protection mode changed to %d", sdata->vif.addr, ht_opmode); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 8c747fa9319b..2749cb86b462 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -61,6 +61,7 @@ const struct mesh_config default_mesh_config = { .dot11MeshGateAnnouncementProtocol = false, .dot11MeshForwarding = true, .rssi_threshold = MESH_RSSI_THRESHOLD, + .ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d5005c59c472..b67b1114e25a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3390,7 +3390,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u8(msg, NL80211_MESHCONF_FORWARDING, cur_params.dot11MeshForwarding) || nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, - cur_params.rssi_threshold)) + cur_params.rssi_threshold) || + nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE, + cur_params.ht_opmode)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -3426,6 +3428,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32}, + [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16}, }; static const struct nla_policy @@ -3523,6 +3526,8 @@ do {\ mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, + mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); if (mask_out) *mask_out = mask; From 341352d13dae752610342923c53ebe461624ee2c Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 8 May 2012 22:18:09 -0400 Subject: [PATCH 41/41] iwlwifi: fix-up some merge damage from commit 0d6c4a2 Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index 0c252c5d8bf1..5b80467a5bb1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -764,7 +764,8 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, fraglen = len - hdrlen; if (fraglen) { - int offset = (void *)hdr - rxb_addr(rxb) + rxb_offset(rxb); + int offset = (void *)hdr + hdrlen - + rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize);