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 #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 {