summarylogtreecommitdiffstats
path: root/ath.patch
diff options
context:
space:
mode:
Diffstat (limited to 'ath.patch')
-rw-r--r--ath.patch476
1 files changed, 476 insertions, 0 deletions
diff --git a/ath.patch b/ath.patch
new file mode 100644
index 000000000000..559820938c9a
--- /dev/null
+++ b/ath.patch
@@ -0,0 +1,476 @@
+diff --git i/drivers/net/wireless/ath/ath9k/htc.h w/drivers/net/wireless/ath/ath9k/htc.h
+index 16dff4b89a86..63eea0bc6097 100644
+--- i/drivers/net/wireless/ath/ath9k/htc.h
++++ w/drivers/net/wireless/ath/ath9k/htc.h
+@@ -604,6 +604,9 @@ void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw);
+
+ struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv);
+
++// TODO make more generic: any argument instead of just 2 int32s
++int dbg_firmware_cmd(struct ath9k_htc_priv *priv, u8 cmd_id, u32 arguments[2]);
++
+ #ifdef CONFIG_MAC80211_LEDS
+ void ath9k_configure_leds(struct ath9k_htc_priv *priv);
+ void ath9k_init_leds(struct ath9k_htc_priv *priv);
+diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_debug.c w/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+index dc79afd7e151..3e0d1a35ef6f 100644
+--- i/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
++++ w/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+@@ -14,6 +14,7 @@
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
++#include <net/genetlink.h>
+ #include "htc.h"
+
+ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf,
+@@ -479,13 +480,298 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
+ WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
+ }
+
++struct ath9k_htc_priv *genl_ath9k_priv = NULL;
++
++enum {
++ ATH9K_HTC_A_UNSPEC,
++ ATH9K_HTC_A_STR,
++ ATH9K_HTC_A_REG,
++ ATH9K_HTC_A_VAL,
++ ATH9K_HTC_A_MAX,
++};
++
++enum {
++ ATH9K_HTC_C_UNSPEC,
++ ATH9K_C_ECHO,
++ ATH9K_C_SET,
++ ATH9K_C_RATE,
++ ATH9K_C_RESET,
++ ATH9K_C_MAX,
++};
++
++/* attribute policy: defines which attribute has which type (e.g int, char * etc)
++ * possible values defined in net/netlink.h
++ */
++static struct nla_policy ath9k_htc_genl_policy[ATH9K_HTC_A_MAX] = {
++ [ATH9K_HTC_A_STR] = { .type = NLA_NUL_STRING },
++ [ATH9K_HTC_A_REG] = { .type = NLA_U32 },
++ [ATH9K_HTC_A_VAL] = { .type = NLA_U32 },
++};
++
++static struct genl_family ath9k_htc_gnl_family = {
++ .id = GENL_ID_GENERATE,
++ .hdrsize = 0,
++ .name = "ATH9K_HTC",
++ .version = 1,
++ .maxattr = ATH9K_HTC_A_MAX-1,
++};
++
++static int ath9k_echo(struct sk_buff *iskb, struct genl_info *info) {
++ struct nlattr *received_str_attr;
++ char *received_str;
++ struct sk_buff *skb;
++ int errc;
++ void *msg_head;
++
++ if (info == NULL)
++ goto out;
++
++ received_str_attr = info->attrs[ATH9K_HTC_A_STR];
++ if (received_str_attr) {
++ received_str = (char *)nla_data(received_str_attr);
++ if (received_str != NULL)
++ printk("ath9k_htc: got %s\n", received_str);
++ else
++ printk(KERN_ERR "error while receiving data\n");
++ } else {
++ printk(KERN_ERR "attribute %i not present\n", ATH9K_HTC_A_STR);
++ }
++
++ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++ if (skb == NULL)
++ goto out;
++
++ msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &ath9k_htc_gnl_family, 0, ATH9K_C_ECHO);
++ if (msg_head == NULL) {
++ errc = -ENOMEM;
++ goto out;
++ }
++
++ errc = nla_put_string(skb, ATH9K_HTC_A_STR, "pong");
++ if (errc != 0)
++ goto out;
++
++ genlmsg_end(skb, msg_head);
++
++ errc = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
++ if (errc != 0)
++ goto out;
++
++ return 0;
++
++out:
++ printk(KERN_ERR "an error occured in ath9k_echo:\n");
++
++ return 1;
++}
++
++static int ath9k_set_reg(struct sk_buff *iskb, struct genl_info *info) {
++ struct nlattr *received_int_attr;
++ u32 *received_reg = NULL;
++ u32 *received_val = NULL;
++ struct sk_buff *skb;
++ int errc;
++ void *msg_head;
++ u32 vals[2];
++
++ if (info == NULL)
++ goto out;
++
++ // TODO repeated code... simplify
++ received_int_attr = info->attrs[ATH9K_HTC_A_REG];
++ if (received_int_attr) {
++ received_reg = (u32*)nla_data(received_int_attr);
++ if(received_reg)
++ printk("ath9k_htc: reg %08x\n", *received_reg);
++ } else {
++ printk(KERN_ERR "attribute %i not present\n", ATH9K_HTC_A_REG);
++ }
++
++ received_int_attr = info->attrs[ATH9K_HTC_A_VAL];
++ if (received_int_attr) {
++ received_val = (u32*)nla_data(received_int_attr);
++ if(received_val)
++ printk("ath9k_htc: val %08x\n", *received_val);
++ } else {
++ printk(KERN_ERR "attribute %i not present\n", ATH9K_HTC_A_VAL);
++ }
++
++ if(received_reg)
++ vals[0] = *received_reg;
++
++ if(received_val)
++ vals[1] = *received_val;
++
++ dbg_firmware_cmd(genl_ath9k_priv, DBG_CMD_SET_REG, vals);
++
++ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++ if (skb == NULL)
++ goto out;
++
++ msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &ath9k_htc_gnl_family, 0, ATH9K_C_SET);
++ if (msg_head == NULL) {
++ errc = -ENOMEM;
++ goto out;
++ }
++
++ errc = nla_put_string(skb, ATH9K_HTC_A_STR, "reg set successfully");
++ if (errc != 0)
++ goto out;
++
++ genlmsg_end(skb, msg_head);
++
++ errc = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
++ if (errc != 0)
++ goto out;
++
++ return 0;
++
++out:
++ printk(KERN_ERR "an error occured\n");
++
++ return 1;
++}
++
++static int ath9k_set_rate(struct sk_buff *iskb, struct genl_info *info) {
++ struct nlattr *received_int_attr;
++ u32 *received_val = NULL;
++ struct sk_buff *skb;
++ int errc;
++ void *msg_head;
++ u32 vals[2];
++
++ if (info == NULL)
++ goto out;
++
++ // TODO repeated code... simplify
++ received_int_attr = info->attrs[ATH9K_HTC_A_VAL];
++ if (received_int_attr) {
++ received_val = (u32*)nla_data(received_int_attr);
++ if(received_val)
++ printk("ath9k_htc: val %08x\n", *received_val);
++ } else {
++ printk(KERN_ERR "attribute %i not present\n", ATH9K_HTC_A_VAL);
++ }
++
++ if(received_val)
++ vals[0] = *received_val;
++
++ dbg_firmware_cmd(genl_ath9k_priv, DBG_CMD_SET_RATE, vals);
++
++ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++ if (skb == NULL)
++ goto out;
++
++ msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &ath9k_htc_gnl_family, 0, ATH9K_C_RATE);
++ if (msg_head == NULL) {
++ errc = -ENOMEM;
++ goto out;
++ }
++
++ errc = nla_put_string(skb, ATH9K_HTC_A_STR, "rate set successfully");
++ if (errc != 0)
++ goto out;
++
++ genlmsg_end(skb, msg_head);
++
++ errc = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
++ if (errc != 0)
++ goto out;
++
++ return 0;
++
++out:
++ printk(KERN_ERR "an error occured\n");
++
++ return 1;
++}
++
++static int ath9k_reset(struct sk_buff *iskb, struct genl_info *info) {
++ struct sk_buff *skb;
++ int errc;
++ void *msg_head;
++ u32 vals[2];
++
++ if (info == NULL)
++ goto out;
++
++ dbg_firmware_cmd(genl_ath9k_priv, DBG_CMD_RESET, vals);
++
++ // TODO repeated code... simplify
++ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
++ if (skb == NULL)
++ goto out;
++
++ msg_head = genlmsg_put(skb, 0, info->snd_seq + 1, &ath9k_htc_gnl_family, 0, ATH9K_C_RESET);
++ if (msg_head == NULL) {
++ errc = -ENOMEM;
++ goto out;
++ }
++
++ errc = nla_put_string(skb, ATH9K_HTC_A_STR, "reset counters");
++ if (errc != 0)
++ goto out;
++
++ genlmsg_end(skb, msg_head);
++
++ errc = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid);
++ if (errc != 0)
++ goto out;
++
++ return 0;
++
++out:
++ printk(KERN_ERR "an error occured\n");
++
++ return 1;
++}
++
++static struct genl_ops ath9k_htc_gnl_ops[] = {
++ {
++ .cmd = ATH9K_C_ECHO,
++ .flags = 0,
++ .policy = ath9k_htc_genl_policy,
++ .doit = ath9k_echo,
++ .dumpit = NULL,
++ },
++ {
++ .cmd = ATH9K_C_SET,
++ .flags = 0,
++ .policy = ath9k_htc_genl_policy,
++ .doit = ath9k_set_reg,
++ .dumpit = NULL,
++ },
++ {
++ .cmd = ATH9K_C_RATE,
++ .flags = 0,
++ .policy = ath9k_htc_genl_policy,
++ .doit = ath9k_set_rate,
++ .dumpit = NULL,
++ },
++ {
++ .cmd = ATH9K_C_RESET,
++ .flags = 0,
++ .policy = ath9k_htc_genl_policy,
++ .doit = ath9k_reset,
++ .dumpit = NULL,
++ },
++};
++
++
+ void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv)
+ {
++ int errc;
+ ath9k_cmn_spectral_deinit_debug(&priv->spec_priv);
++
++ errc = genl_unregister_family(&ath9k_htc_gnl_family);
++ if(errc != 0){
++ printk(KERN_ERR "unregister family %i\n", errc);
++ }
+ }
+
+ int ath9k_htc_init_debug(struct ath_hw *ah)
+ {
++ int errc;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+@@ -520,5 +806,14 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
+ ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
+ ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
+
++ /* ath9k_htc netlink interface */
++ // TODO only works with one interface at the same time!
++ genl_ath9k_priv = priv;
++ errc = genl_register_family_with_ops(&ath9k_htc_gnl_family, ath9k_htc_gnl_ops);
++ if (errc != 0) {
++ printk("genl_register_family_with_ops error\n");
++ return 1;
++ }
++
+ return 0;
+ }
+diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_init.c w/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+index defacc6c9c99..df7ddfde9423 100644
+--- i/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ w/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -231,6 +231,59 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
+ ath9k_hw_regulatory(priv->ah));
+ }
+
++static void fancy_printk(u8* buffer, u8 id, u8 len) {
++ u8 temp[400];
++ int i;
++ int counth = 0;
++ int countc = 0;
++
++ for(i = 0; i < len; i++) {
++ sprintf(temp + (counth * 3), "%02x ", buffer[i]);
++ counth++;
++ }
++
++ sprintf(temp + (counth * 3), "| ");
++
++ for(i = 0; i < len; i++) {
++ if((buffer[i] > '!' && buffer[i] < '~') || buffer[i] == ' ') {
++ sprintf(temp + (counth * 3) + 2 + countc, "%c", buffer[i]);
++ } else {
++ sprintf(temp + (counth * 3) + 2 + countc, "%c", '.');
++ }
++ countc++;
++ }
++ printk(KERN_ERR "firmware: %s (cmd %d)\n", temp, id);
++}
++
++int dbg_firmware_cmd(struct ath9k_htc_priv *priv, u8 cmd_id, u32 arguments[2])
++{
++ struct dbg_cmd_request cmd;
++ struct dbg_cmd_response cmd_rsp;
++ int ret;
++
++ // Prepare command
++ memset(&cmd, 0, sizeof(cmd));
++ cmd.id = cmd_id;
++ if(arguments != NULL) {
++ cmd.args[0] = cpu_to_be32(arguments[0]);
++ cmd.args[1] = cpu_to_be32(arguments[1]);
++ }
++
++ // Prepare response
++ memset(&cmd_rsp, 0, sizeof(cmd_rsp));
++
++ do {
++ ret = ath9k_wmi_cmd(priv->wmi, WMI_DBGCMD_CMDID, (u8 *)&cmd, sizeof(cmd), (u8 *)&cmd_rsp, sizeof(cmd_rsp), HZ*2);
++ if (ret)
++ return -EINVAL;
++
++ if(cmd_rsp.length > 0)
++ fancy_printk(cmd_rsp.buffer, cmd_id, cmd_rsp.length);
++ } while(cmd_rsp.length != 0);
++
++ return 0;
++}
++
+ static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset)
+ {
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+@@ -249,6 +302,9 @@ static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset)
+ return -EIO;
+ }
+
++ // Piggyback reading of firmware print buffer
++ dbg_firmware_cmd(priv, DBG_CMD_READ_MEMORY, NULL);
++
+ return be32_to_cpu(val);
+ }
+
+diff --git i/drivers/net/wireless/ath/ath9k/wmi.c w/drivers/net/wireless/ath/ath9k/wmi.c
+index 64a354fa78ab..f34e0d6007a5 100644
+--- i/drivers/net/wireless/ath/ath9k/wmi.c
++++ w/drivers/net/wireless/ath/ath9k/wmi.c
+@@ -83,6 +83,11 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
+ return "WMI_RX_STATS_CMDID";
+ case WMI_BITRATE_MASK_CMDID:
+ return "WMI_BITRATE_MASK_CMDID";
++ case WMI_REG_RMW_CMDID:
++ return "WMI_REG_RMW_CMDID"; // Bugfix
++ // New stuff
++ case WMI_DBGCMD_CMDID:
++ return "WMI_DBGCMD_CMDID";
+ }
+
+ return "Bogus";
+@@ -334,6 +339,7 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
+ if (!time_left) {
+ ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n",
+ wmi_cmd_to_name(cmd_id));
++ printk(KERN_ERR "ath9k_htc: Timeout waiting for command %s", wmi_cmd_to_name(cmd_id));
+ mutex_unlock(&wmi->op_mutex);
+ return -ETIMEDOUT;
+ }
+diff --git i/drivers/net/wireless/ath/ath9k/wmi.h w/drivers/net/wireless/ath/ath9k/wmi.h
+index 380175d5ecd7..0f9518b3ea1c 100644
+--- i/drivers/net/wireless/ath/ath9k/wmi.h
++++ w/drivers/net/wireless/ath/ath9k/wmi.h
+@@ -41,6 +41,25 @@ struct wmi_event_swba {
+ u8 beacon_pending;
+ } __packed;
+
++// New stuff
++typedef enum {
++ DBG_CMD_READ_MEMORY,
++ DBG_CMD_TEST,
++ DBG_CMD_SET_REG,
++ DBG_CMD_SET_RATE,
++ DBG_CMD_RESET
++} DBG_CMD_ID;
++
++struct dbg_cmd_request {
++ u8 id;
++ __be32 args[2];
++} __packed;
++
++struct dbg_cmd_response {
++ u8 length;
++ u8 buffer[33]; // WMI does not allow big packets
++} __packed;
++
+ /*
+ * 64 - HTC header - WMI header - 1 / txstatus
+ * And some other hdr. space is also accounted for.
+@@ -113,6 +132,7 @@ enum wmi_cmd_id {
+ WMI_RX_STATS_CMDID,
+ WMI_BITRATE_MASK_CMDID,
+ WMI_REG_RMW_CMDID,
++ WMI_DBGCMD_CMDID,
+ };
+
+ enum wmi_event_id {