static int ath6kl_configure_vreg(struct ath6kl_power_vreg_data *vreg) { int rc = 0; ath6kl_dbg(ATH6KL_DBG_BOOT, "config %s\n", vreg->name); /* Get the regulator handle for vreg */ if (!(vreg->reg)) { rc = ath6kl_vreg_init(vreg); if (rc < 0) return rc; } rc = ath6kl_vreg_enable(vreg); if (rc < 0) return rc; return rc; }
void ath6kl_ap_keepalive_deinit(struct ath6kl_vif *vif) { struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; if (ap_keepalive) { if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) del_timer(&ap_keepalive->ap_ka_timer); kfree(ap_keepalive); } vif->ap_keepalive_ctx = NULL; ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, "ap_keepalive deinit (vif idx %d)\n", vif->fw_vif_idx); return; }
int rttm_issue_request(void *buf, u32 len) { struct rttm_context *prttm = NULL; struct nsp_header hdr; u32 ftype; struct ath6kl *ar = NULL; enum wmi_cmd_id cmd_id = WMI_RTT_MEASREQ_CMDID; prttm = DEV_GETRTT_HDL(); ar = prttm->ar; memcpy(&hdr, buf, NSP_HDR_LEN); ftype = hdr.frame_type; ath6kl_dbg(ATH6KL_DBG_RTT, "RTT Req Type : %d Len : %d ", ftype, len); if (ftype == NSP_MRQST) { struct nsp_mrqst *pstmrqst = (struct nsp_mrqst *)(buf + NSP_HDR_LEN); ath6kl_dbg(ATH6KL_DBG_RTT, "NSP Request ID:%d mode:%d " "channel : %d NoMeas : %d Rate : %x\n ", pstmrqst->request_id, pstmrqst->mode, pstmrqst->channel, pstmrqst->no_of_measurements, pstmrqst->transmit_rate); if (pstmrqst->no_of_measurements > 10) { ath6kl_dbg(ATH6KL_DBG_RTT, "RTTREQ No Measurements >10 : %d ", pstmrqst->no_of_measurements); return -EINVAL; } cmd_id = WMI_RTT_MEASREQ_CMDID; } else if (ftype == NSP_RTTCONFIG) { struct nsp_rtt_config *pstrttcfg = (struct nsp_rtt_config *)(buf + NSP_HDR_LEN); ath6kl_dbg(ATH6KL_DBG_RTT, "NSP RTTCFG 2gCal%d 5gCal:%d " "FFTScale: %d RngScale:%d " "CLKSpeed2g : %d\n CLKSpeed5g : %d", pstrttcfg->ClkCal[0], pstrttcfg->ClkCal[1], pstrttcfg->FFTScale, pstrttcfg->RangeScale, pstrttcfg->ClkSpeed[0], pstrttcfg->ClkSpeed[1]); cmd_id = WMI_RTT_CONFIG_CMDID; } else if (ftype == NSP_RTTCLKCAL_INFO) { ath6kl_dbg(ATH6KL_DBG_RTT, "NSP CLK CALINFO CMD\n"); cmd_id = WMI_RTT_CLKCALINFO_CMDID; } if (wmi_rtt_req(ar->wmi, cmd_id, buf + NSP_HDR_LEN, len - NSP_HDR_LEN)) { ath6kl_dbg(ATH6KL_DBG_RTT, "RTT Req Fail "); return -EIO; } return 0; }
int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) { u32 cid = BMI_READ_SOC_REGISTER; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); if (ret) { ath6kl_err("Unable to read from the device: %d\n", ret); return ret; } memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); return 0; }
/* * Set HTC/Mbox operational parameters, this can only be called when the * target is in the BMI phase. */ static int ath6kl_set_htc_params(struct ath6kl *ar, u32 mbox_isr_yield_val, u8 htc_ctrl_buf) { int status; u32 blk_size; blk_size = ar->mbox_info.block_size; if (htc_ctrl_buf) blk_size |= ((u32)htc_ctrl_buf) << 16; /* set the host interest area for the block size */ status = ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz)), (u8 *)&blk_size, 4); if (status) { ath6kl_err("bmi_write_memory for IO block size failed\n"); goto out; } ath6kl_dbg(ATH6KL_DBG_TRC, "block size set: %d (target addr:0x%X)\n", blk_size, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_io_block_sz))); if (mbox_isr_yield_val) { /* set the host interest area for the mbox ISR yield limit */ status = ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_mbox_isr_yield_limit)), (u8 *)&mbox_isr_yield_val, 4); if (status) { ath6kl_err("bmi_write_memory for yield limit failed\n"); goto out; } } out: return status; }
int ath6kl_bmi_done(struct ath6kl *ar) { int ret; u32 cid = BMI_DONE; if (ar->bmi.done_sent) { ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); return 0; } ar->bmi.done_sent = true; ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); if (ret) { ath6kl_err("Unable to send bmi done: %d\n", ret); return ret; } return 0; }
static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); int ret; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: changed 0x%x\n", __func__, changed); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (changed & WIPHY_PARAM_RTS_THRESHOLD) { ret = ath6kl_wmi_set_rts_cmd(ar->wmi, wiphy->rts_threshold); if (ret != 0) { ath6kl_err("ath6kl_wmi_set_rts_cmd failed\n"); return -EIO; } } return 0; }
void ath6kl_mangle_mac_address(struct ath6kl *ar, u8 locally_administered_bit) { u8 *ptr_mac; int i, ret; switch (ar->target_type) { case TARGET_TYPE_AR6003: ptr_mac = ar->fw_board + AR6003_MAC_ADDRESS_OFFSET; break; case TARGET_TYPE_AR6004: ptr_mac = ar->fw_board + AR6004_MAC_ADDRESS_OFFSET; break; default: ath6kl_err("Invalid Target Type\n"); return; } ath6kl_dbg(ATH6KL_DBG_BOOT, "MAC from EEPROM %02X:%02X:%02X:%02X:%02X:%02X\n", ptr_mac[0], ptr_mac[1], ptr_mac[2], ptr_mac[3], ptr_mac[4], ptr_mac[5]); ret = ath6kl_fetch_mac_file(ar); if (ret) { ath6kl_err("MAC address file not found\n"); return; } for (i = 0; i < ETH_ALEN; ++i) { ptr_mac[i] = ath6kl_softmac[i] & 0xff; } kfree(ath6kl_softmac); if (locally_administered_bit) ptr_mac[0] |= 0x02; ath6kl_calculate_crc(ar->target_type, ar->fw_board, ar->fw_board_len); }
int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) { u32 cid = BMI_WRITE_SOC_REGISTER; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr) + sizeof(param); if (size > MAX_BMI_CMDBUF_SZ) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi write SOC reg: addr: 0x%x, param: %d\n", addr, param); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); offset += sizeof(param); ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to write to the device: %d\n", ret); return ret; } return 0; }
static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast) { u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto; u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : &ar->grp_crypto_len; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n", __func__, cipher, ucast); switch (cipher) { case 0: /* our own hack to use value 0 as no crypto used */ *ar_cipher = NONE_CRYPT; *ar_cipher_len = 0; break; case WLAN_CIPHER_SUITE_WEP40: *ar_cipher = WEP_CRYPT; *ar_cipher_len = 5; break; case WLAN_CIPHER_SUITE_WEP104: *ar_cipher = WEP_CRYPT; *ar_cipher_len = 13; break; case WLAN_CIPHER_SUITE_TKIP: *ar_cipher = TKIP_CRYPT; *ar_cipher_len = 0; break; case WLAN_CIPHER_SUITE_CCMP: *ar_cipher = AES_CRYPT; *ar_cipher_len = 0; break; default: ath6kl_err("cipher 0x%x not supported\n", cipher); return -ENOTSUPP; } return 0; }
static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ath6kl *ar = ath6kl_priv(ndev); struct wireless_dev *wdev = ar->wdev; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); if (!ath6kl_cfg80211_ready(ar)) return -EIO; switch (type) { case NL80211_IFTYPE_STATION: ar->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: ar->next_mode = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: ar->next_mode = AP_NETWORK; break; case NL80211_IFTYPE_P2P_CLIENT: ar->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_P2P_GO: ar->next_mode = AP_NETWORK; break; default: ath6kl_err("invalid interface type %u\n", type); return -EOPNOTSUPP; } wdev->iftype = type; return 0; }
int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) { u32 cid = BMI_LZ_STREAM_START; int ret; u32 offset; u16 size; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } size = sizeof(cid) + sizeof(addr); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; } memset(ar->bmi.cmd_buf, 0, size); ath6kl_dbg(ATH6KL_DBG_BMI, "bmi LZ stream start: addr: 0x%x)\n", addr); offset = 0; memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); offset += sizeof(cid); memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); offset += sizeof(addr); ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); if (ret) { ath6kl_err("Unable to start LZ stream to the device: %d\n", ret); return ret; } return 0; }
static int ath6kl_vreg_init(struct ath6kl_power_vreg_data *vreg) { int rc = 0; struct device *dev = &(gpdata->pdev->dev); ath6kl_dbg(ATH6KL_DBG_BOOT, "vreg_get for : %s\n", vreg->name); /* Get the regulator handle */ vreg->reg = regulator_get(dev, vreg->name); if (IS_ERR(vreg->reg)) { rc = PTR_ERR(vreg->reg); ath6kl_err("%s: regulator_get(%s) failed. rc=%d\n", __func__, vreg->name, rc); goto out; } if ((regulator_count_voltages(vreg->reg) > 0) && (vreg->low_vol_level) && (vreg->high_vol_level)) vreg->set_voltage_sup = 1; out: return rc; }
void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast) { struct ath6kl_sta *sta; struct ath6kl *ar = vif->ar; u8 tsc[6]; /* * For AP case, keyid will have aid of STA which sent pkt with * MIC error. Use this aid to get MAC & send it to hostapd. */ if (vif->nw_type == AP_NETWORK) { sta = ath6kl_find_sta_by_aid(ar, (keyid >> 2)); if (!sta) return; ath6kl_dbg(ATH6KL_DBG_TRC, "ap tkip mic error received from aid=%d\n", keyid); memset(tsc, 0, sizeof(tsc)); /* FIX: get correct TSC */ cfg80211_michael_mic_failure(vif->ndev, sta->mac, NL80211_KEYTYPE_PAIRWISE, keyid, tsc, GFP_KERNEL); } else
void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) { struct ath6kl *ar = devt; memcpy(ar->mac_addr, datap, ETH_ALEN); ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n", __func__, ar->mac_addr); ar->version.wlan_ver = sw_ver; ar->version.abi_ver = abi_ver; snprintf(ar->wiphy->fw_version, sizeof(ar->wiphy->fw_version), "%u.%u.%u.%u", (ar->version.wlan_ver & 0xf0000000) >> 28, (ar->version.wlan_ver & 0x0f000000) >> 24, (ar->version.wlan_ver & 0x00ff0000) >> 16, (ar->version.wlan_ver & 0x0000ffff)); /* indicate to the waiting thread that the ready event was received */ set_bit(WMI_READY, &ar->flag); wake_up(&ar->event_wq); }
int ath6kl_ap_keepalive_start(struct ath6kl_vif *vif) { struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; BUG_ON(!ap_keepalive); BUG_ON(vif->nw_type != AP_NETWORK); ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, "ap_keepalive start (vif idx %d) flags %x\n", vif->fw_vif_idx, ap_keepalive->flags); if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) { mod_timer(&ap_keepalive->ap_ka_timer, jiffies + msecs_to_jiffies(ap_keepalive->ap_ka_interval) - msecs_to_jiffies(ATH6KL_AP_KA_PRELOAD_STAT_TIME)); ap_keepalive->flags |= (ATH6KL_AP_KA_FLAGS_START | ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); } return 0; }
int ath6kl_hsic_bind(int bind) { char buf[16]; int length; ath6kl_dbg(ATH6KL_DBG_BOOT, "%s, bind: %d\n", __func__, bind); if (bind) { length = snprintf(buf, sizeof(buf), "%s\n", "msm_hsic_host"); android_readwrite_file( "/sys/bus/platform/drivers/msm_hsic_host/bind", NULL, buf, length); } else { length = snprintf(buf, sizeof(buf), "%s\n", "msm_hsic_host"); android_readwrite_file( "/sys/bus/platform/drivers/msm_hsic_host/unbind", NULL, buf, length); } return 0; }
int ath6kl_hif_setup(struct ath6kl_device *dev) { int status = 0; spin_lock_init(&dev->lock); /* * NOTE: we actually get the block size of a mailbox other than 0, * for SDIO the block size on mailbox 0 is artificially set to 1. * So we use the block size that is set for the other 3 mailboxes. */ dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size; /* must be a power of 2 */ if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) { WARN_ON(1); status = -EINVAL; goto fail_setup; } /* assemble mask, used for padding to a block */ dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1; ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); /* usb doesn't support enabling interrupts */ /* FIXME: remove check once USB support is implemented */ if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) return 0; status = ath6kl_hif_disable_intrs(dev); fail_setup: return status; }
u32 ath6kl_ap_keepalive_get_inactive_time(struct ath6kl_vif *vif, u8 *mac) { struct ath6kl_sta *conn; u32 inact_time; conn = ath6kl_find_sta(vif, mac); if (conn) { inact_time = ap_keepalive_get_inactive_time(vif, conn); } else { inact_time = 0; /* FIXME : return -1 ? */ ath6kl_err("can't find sta %02x:%02x:%02x:%02x:%02x:%02x vif-idx %d\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], vif->fw_vif_idx); } ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, "ap_keepalive inact aid %d inact_time %d ms \n", (conn ? (conn->aid) : 0), inact_time); return inact_time; }
int ath6kl_configure_target(struct ath6kl *ar) { u32 param, ram_reserved_size; u8 fw_iftype; fw_iftype = ath6kl_get_fw_iftype(ar); if (fw_iftype == 0xff) return -EINVAL; /* Tell target which HTC version it is used*/ param = HTC_PROTOCOL_VERSION; if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest)), (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_write_memory for htc version failed\n"); return -EIO; } /* set the firmware mode to STA/IBSS/AP */ param = 0; if (ath6kl_bmi_read(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag)), (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_read_memory for setting fwmode failed\n"); return -EIO; } param |= (1 << HI_OPTION_NUM_DEV_SHIFT); param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT); param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_option_flag)), (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_write_memory for setting fwmode failed\n"); return -EIO; } ath6kl_dbg(ATH6KL_DBG_TRC, "firmware mode set\n"); /* * Hardcode the address use for the extended board data * Ideally this should be pre-allocate by the OS at boot time * But since it is a new feature and board data is loaded * at init time, we have to workaround this from host. * It is difficult to patch the firmware boot code, * but possible in theory. */ if (ar->target_type == TARGET_TYPE_AR6003) { if (ar->version.target_ver == AR6003_REV2_VERSION) { param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; ram_reserved_size = AR6003_REV2_RAM_RESERVE_SIZE; } else { param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; ram_reserved_size = AR6003_REV3_RAM_RESERVE_SIZE; } if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_ext_data)), (u8 *)¶m, 4) != 0) { ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); return -EIO; } if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_end_ram_reserve_sz)), (u8 *)&ram_reserved_size, 4) != 0) { ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); return -EIO; } } /* set the block size for the target */ if (ath6kl_set_htc_params(ar, MBOX_YIELD_LIMIT, 0)) /* use default number of control buffers */ return -EIO; return 0; }
int ath6kl_core_init(struct ath6kl *ar) { int ret = 0; struct ath6kl_bmi_target_info targ_info; ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); if (!ar->ath6kl_wq) return -ENOMEM; ret = ath6kl_bmi_init(ar); if (ret) goto err_wq; ret = ath6kl_bmi_get_target_info(ar, &targ_info); if (ret) goto err_bmi_cleanup; ar->version.target_ver = le32_to_cpu(targ_info.version); ar->target_type = le32_to_cpu(targ_info.type); ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version); ret = ath6kl_configure_target(ar); if (ret) goto err_bmi_cleanup; ar->htc_target = ath6kl_htc_create(ar); if (!ar->htc_target) { ret = -ENOMEM; goto err_bmi_cleanup; } ar->aggr_cntxt = aggr_init(ar->net_dev); if (!ar->aggr_cntxt) { ath6kl_err("failed to initialize aggr\n"); ret = -ENOMEM; goto err_htc_cleanup; } ret = ath6kl_init_upload(ar); if (ret) goto err_htc_cleanup; ret = ath6kl_init(ar->net_dev); if (ret) goto err_htc_cleanup; /* This runs the init function if registered */ ret = register_netdev(ar->net_dev); if (ret) { ath6kl_err("register_netdev failed\n"); ath6kl_destroy(ar->net_dev, 0); return ret; } set_bit(NETDEV_REGISTERED, &ar->flag); ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, ar->net_dev->name, ar->net_dev, ar); return ret; err_htc_cleanup: ath6kl_htc_cleanup(ar->htc_target); err_bmi_cleanup: ath6kl_bmi_cleanup(ar); err_wq: destroy_workqueue(ar->ath6kl_wq); return ret; }
static int ath6kl_init(struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); int status = 0; s32 timeleft; if (!ar) return -EIO; /* Do we need to finish the BMI phase */ if (ath6kl_bmi_done(ar)) { status = -EIO; goto ath6kl_init_done; } /* Indicate that WMI is enabled (although not ready yet) */ set_bit(WMI_ENABLED, &ar->flag); ar->wmi = ath6kl_wmi_init(ar); if (!ar->wmi) { ath6kl_err("failed to initialize wmi\n"); status = -EIO; goto ath6kl_init_done; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); wlan_node_table_init(&ar->scan_table); /* * The reason we have to wait for the target here is that the * driver layer has to init BMI in order to set the host block * size. */ if (ath6kl_htc_wait_target(ar->htc_target)) { status = -EIO; goto err_node_cleanup; } if (ath6kl_init_service_ep(ar)) { status = -EIO; goto err_cleanup_scatter; } /* setup access class priority mappings */ ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ ar->ac_stream_pri_map[WMM_AC_BE] = 1; ar->ac_stream_pri_map[WMM_AC_VI] = 2; ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ /* give our connected endpoints some buffers */ ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); /* allocate some buffers that handle larger AMSDU frames */ ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); /* setup credit distribution */ ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info); ath6kl_cookie_init(ar); /* start HTC */ status = ath6kl_htc_start(ar->htc_target); if (status) { ath6kl_cookie_cleanup(ar); goto err_rxbuf_cleanup; } /* Wait for Wmi event to be ready */ timeleft = wait_event_interruptible_timeout(ar->event_wq, test_bit(WMI_READY, &ar->flag), WMI_TIMEOUT); if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ATH6KL_ABI_VERSION, ar->version.abi_ver); status = -EIO; goto err_htc_stop; } if (!timeleft || signal_pending(current)) { ath6kl_err("wmi is not ready or wait was interrupted\n"); status = -EIO; goto err_htc_stop; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); /* communicate the wmi protocol verision to the target */ if ((ath6kl_set_host_app_area(ar)) != 0) ath6kl_err("unable to set the host app area\n"); ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; status = ath6kl_target_config_wlan_params(ar); if (!status) goto ath6kl_init_done; err_htc_stop: ath6kl_htc_stop(ar->htc_target); err_rxbuf_cleanup: ath6kl_htc_flush_rx_buf(ar->htc_target); ath6kl_cleanup_amsdu_rxbufs(ar); err_cleanup_scatter: ath6kl_hif_cleanup_scatter(ar); err_node_cleanup: wlan_node_table_cleanup(&ar->scan_table); ath6kl_wmi_shutdown(ar->wmi); clear_bit(WMI_ENABLED, &ar->flag); ar->wmi = NULL; ath6kl_init_done: return status; }
static void get_htc_packet_credit_based(struct htc_target *target, struct htc_endpoint *ep, struct list_head *queue) { int credits_required; int remainder; u8 send_flags; struct htc_packet *packet; unsigned int transfer_len; /* NOTE : the TX lock is held when this function is called */ /* loop until we can grab as many packets out of the queue as we can */ while (true) { send_flags = 0; if (list_empty(&ep->txq)) break; /* get packet at head, but don't remove it */ packet = list_first_entry(&ep->txq, struct htc_packet, list); if (packet == NULL) break; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: got head packet:0x%p , queue depth: %d\n", __func__, packet, get_queue_depth(&ep->txq)); transfer_len = packet->act_len + HTC_HDR_LENGTH; if (transfer_len <= target->tgt_cred_sz) { credits_required = 1; } else { /* figure out how many credits this message requires */ credits_required = transfer_len / target->tgt_cred_sz; remainder = transfer_len % target->tgt_cred_sz; if (remainder) credits_required++; } ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n", __func__, credits_required, ep->cred_dist.credits); if (ep->eid == ENDPOINT_0) { /* * endpoint 0 is special, it always has a credit and * does not require credit based flow control */ credits_required = 0; } else { if (ep->cred_dist.credits < credits_required) break; ep->cred_dist.credits -= credits_required; ep->ep_st.cred_cosumd += credits_required; /* check if we need credits back from the target */ if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { /* tell the target we need credits ASAP! */ send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; ep->ep_st.cred_low_indicate += 1; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: host needs credits\n", __func__); } } /* now we can fully dequeue */ packet = list_first_entry(&ep->txq, struct htc_packet, list); list_del(&packet->list); /* save the number of credits this packet consumed */ packet->info.tx.cred_used = credits_required; /* save send flags */ packet->info.tx.flags = send_flags; packet->info.tx.seqno = ep->seqno; ep->seqno++; /* queue this packet into the caller's queue */ list_add_tail(&packet->list, queue); } }
static enum htc_send_queue_result htc_try_send(struct htc_target *target, struct htc_endpoint *ep, struct list_head *txq) { struct list_head send_queue; /* temp queue to hold packets */ struct htc_packet *packet, *tmp_pkt; struct ath6kl *ar = target->dev->ar; enum htc_send_full_action action; int tx_resources, overflow, txqueue_depth, i, good_pkts; u8 pipeid; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n", __func__, txq, (txq == NULL) ? 0 : get_queue_depth(txq)); /* init the local send queue */ INIT_LIST_HEAD(&send_queue); /* * txq equals to NULL means * caller didn't provide a queue, just wants us to * check queues and send */ if (txq != NULL) { if (list_empty(txq)) { /* empty queue */ return HTC_SEND_QUEUE_DROP; } spin_lock_bh(&target->tx_lock); txqueue_depth = get_queue_depth(&ep->txq); spin_unlock_bh(&target->tx_lock); if (txqueue_depth >= ep->max_txq_depth) { /* we've already overflowed */ overflow = get_queue_depth(txq); } else { /* get how much we will overflow by */ overflow = txqueue_depth; overflow += get_queue_depth(txq); /* get how much we will overflow the TX queue by */ overflow -= ep->max_txq_depth; } /* if overflow is negative or zero, we are okay */ if (overflow > 0) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n", __func__, ep->eid, overflow, txqueue_depth, ep->max_txq_depth); } if ((overflow <= 0) || (ep->ep_cb.tx_full == NULL)) { /* * all packets will fit or caller did not provide send * full indication handler -- just move all of them * to the local send_queue object */ list_splice_tail_init(txq, &send_queue); } else { good_pkts = get_queue_depth(txq) - overflow; if (good_pkts < 0) { WARN_ON_ONCE(1); return HTC_SEND_QUEUE_DROP; } /* we have overflowed, and a callback is provided */ /* dequeue all non-overflow packets to the sendqueue */ for (i = 0; i < good_pkts; i++) { /* pop off caller's queue */ packet = list_first_entry(txq, struct htc_packet, list); list_del(&packet->list); /* insert into local queue */ list_add_tail(&packet->list, &send_queue); } /* * the caller's queue has all the packets that won't fit * walk through the caller's queue and indicate each to * the send full handler */ list_for_each_entry_safe(packet, tmp_pkt, txq, list) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Indicat overflowed TX pkts: %p\n", __func__, packet); action = ep->ep_cb.tx_full(ep->target, packet); if (action == HTC_SEND_FULL_DROP) { /* callback wants the packet dropped */ ep->ep_st.tx_dropped += 1; /* leave this one in the caller's queue * for cleanup */ } else { /* callback wants to keep this packet, * remove from caller's queue */ list_del(&packet->list); /* put it in the send queue */ list_add_tail(&packet->list, &send_queue); } } if (list_empty(&send_queue)) { /* no packets made it in, caller will cleanup */ return HTC_SEND_QUEUE_DROP; } } }
int ath6kl_ap_keepalive_config(struct ath6kl_vif *vif, u32 ap_ka_interval, u32 ap_ka_reclaim_cycle) { struct ap_keepalive_info *ap_keepalive = vif->ap_keepalive_ctx; int restart = 0; if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_BY_SUPP) { ath6kl_dbg(ATH6KL_DBG_INFO, "already offlad to supplicant/hostapd, bypass it.\n"); return 0; } /* FIXME : the better code protection by the spin-lock. */ if ((ap_ka_interval != 0) && (ap_ka_interval < ATH6KL_AP_KA_INTERVAL_MIN)) ap_ka_interval = ATH6KL_AP_KA_INTERVAL_MIN; if (ap_ka_reclaim_cycle == 0) ap_ka_reclaim_cycle = 1; if (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_START) { del_timer(&ap_keepalive->ap_ka_timer); ap_keepalive->flags &= ~(ATH6KL_AP_KA_FLAGS_START | ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); restart = 1; } if (ap_ka_interval == 0) { ap_keepalive->ap_ka_interval = 0; ap_keepalive->ap_ka_reclaim_cycle = 0; ap_keepalive->flags &= ~ATH6KL_AP_KA_FLAGS_ENABLED; } else { if (ap_ka_interval * ap_ka_reclaim_cycle < ATH6KL_AP_KA_RECLAIM_TIME_MAX) { ap_keepalive->ap_ka_interval = ap_ka_interval; ap_keepalive->ap_ka_reclaim_cycle = ap_ka_reclaim_cycle; } else { ap_keepalive->ap_ka_interval = ATH6KL_AP_KA_INTERVAL_DEFAULT; ap_keepalive->ap_ka_reclaim_cycle = ATH6KL_AP_KA_RECLAIM_CYCLE_DEFAULT; } ap_keepalive->ap_ka_remove_time = ap_keepalive->ap_ka_interval * ap_keepalive->ap_ka_reclaim_cycle; ap_keepalive->flags |= ATH6KL_AP_KA_FLAGS_ENABLED; if (restart) { mod_timer(&ap_keepalive->ap_ka_timer, jiffies + msecs_to_jiffies(ap_keepalive->ap_ka_interval) - msecs_to_jiffies(ATH6KL_AP_KA_PRELOAD_STAT_TIME * 1000)); ap_keepalive->flags |= (ATH6KL_AP_KA_FLAGS_START | ATH6KL_AP_KA_FLAGS_PRELOAD_STAT); } } ath6kl_dbg(ATH6KL_DBG_KEEPALIVE, "ap_keepalive config (vif idx %d interval %d cycle %d %s restart %d)\n", vif->fw_vif_idx, ap_keepalive->ap_ka_interval, ap_keepalive->ap_ka_reclaim_cycle, (ap_keepalive->flags & ATH6KL_AP_KA_FLAGS_ENABLED) ? "ON" : "OFF", restart); return 0; }
static bool ath6kl_parse_data_pkt_for_wake_lock(struct ath6kl *ar, struct sk_buff *skb) { struct net_device *ndev; struct ath6kl_vif *vif; struct ethhdr *hdr; bool need_wake = false; u16 dst_port; vif = ath6kl_vif_first(ar); if (!vif) return need_wake; if (skb->len < sizeof(struct ethhdr)) return need_wake; hdr = (struct ethhdr *) skb->data; #if 0 // by bbelief if (test_and_clear_bit(WOW_RESUME_PRINT, &ar->flag)) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "(wow) dest mac:%pM, src mac:%pM, type/len :%04x\n", hdr->h_dest, hdr->h_source, be16_to_cpu(hdr->h_proto)); } #endif if (!is_multicast_ether_addr(hdr->h_dest)) { switch (ntohs(hdr->h_proto)) { case 0x0800: /* IP */ need_wake = ath6kl_parse_ip_pkt_for_wake_lock(skb); break; case 0x888e: /* EAPOL */ case 0x88c7: /* RSN_PREAUTH */ case 0x88b4: /* WAPI */ need_wake = true; break; default: break; } } else if (!is_broadcast_ether_addr(hdr->h_dest)) { if (skb->len >= 14 + 20) { /* mDNS packets */ u8 *dst_ipaddr = (u8 *)(skb->data + 14 + 20 - 4); ndev = vif->ndev; if (((dst_ipaddr[3] & 0xf8) == 0xf8) && (vif->nw_type == AP_NETWORK || (ndev->flags & IFF_ALLMULTI || ndev->flags & IFF_MULTICAST))) need_wake = true; } } else if (vif->nw_type == AP_NETWORK) { switch (ntohs(hdr->h_proto)) { case 0x0800: /* IP */ if (skb->len >= 14 + 20 + 2) { dst_port = *(u16 *)(skb->data + 14 + 20); /* dhcp req */ need_wake = (ntohs(dst_port) == 0x43); } break; case 0x0806: need_wake = true; default: break; } } return need_wake; }
int ath6kl_bmi_get_target_info(struct ath6kl *ar, struct ath6kl_bmi_target_info *targ_info) { int ret; u32 cid = BMI_GET_TARGET_INFO; if (ar->bmi.done_sent) { ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); return -EACCES; } ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); if (ret) { ath6kl_err("Unable to send get target info: %d\n", ret); return ret; } if (ar->hif_type == ATH6KL_HIF_TYPE_USB) { ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info, sizeof(*targ_info)); } else { ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version, sizeof(targ_info->version)); } if (ret) { ath6kl_err("Unable to recv target info: %d\n", ret); return ret; } if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->byte_count, sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("unable to read target info byte count: %d\n", ret); return ret; } if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { WARN_ON(1); return -EINVAL; } ret = ath6kl_hif_bmi_read(ar, ((u8 *)targ_info) + sizeof(targ_info->byte_count), sizeof(*targ_info) - sizeof(targ_info->byte_count)); if (ret) { ath6kl_err("Unable to read target info (%d bytes): %d\n", targ_info->byte_count, ret); return ret; } } ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", targ_info->version, targ_info->type); return 0; }
static int ath6kl_upload_board_file(struct ath6kl *ar) { u32 board_address, board_ext_address, param; int ret; if (ar->fw_board == NULL) { ret = ath6kl_fetch_board_file(ar); if (ret) return ret; } /* Determine where in Target RAM to write Board Data */ ath6kl_bmi_read(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_data)), (u8 *) &board_address, 4); ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n", board_address); /* determine where in target ram to write extended board data */ ath6kl_bmi_read(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_ext_data)), (u8 *) &board_ext_address, 4); ath6kl_dbg(ATH6KL_DBG_TRC, "board file download addr: 0x%x\n", board_ext_address); if (board_ext_address == 0) { ath6kl_err("Failed to get board file target address.\n"); return -EINVAL; } if (ar->fw_board_len == (AR6003_BOARD_DATA_SZ + AR6003_BOARD_EXT_DATA_SZ)) { /* write extended board data */ ret = ath6kl_bmi_write(ar, board_ext_address, ar->fw_board + AR6003_BOARD_DATA_SZ, AR6003_BOARD_EXT_DATA_SZ); if (ret) { ath6kl_err("Failed to write extended board data: %d\n", ret); return ret; } /* record that extended board data is initialized */ param = (AR6003_BOARD_EXT_DATA_SZ << 16) | 1; ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_ext_data_config)), (unsigned char *) ¶m, 4); } if (ar->fw_board_len < AR6003_BOARD_DATA_SZ) { ath6kl_err("Too small board file: %zu\n", ar->fw_board_len); ret = -EINVAL; return ret; } ret = ath6kl_bmi_write(ar, board_address, ar->fw_board, AR6003_BOARD_DATA_SZ); if (ret) { ath6kl_err("Board file bmi write failed: %d\n", ret); return ret; } /* record the fact that Board Data IS initialized */ param = 1; ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_board_data_initialized)), (u8 *)¶m, 4); return ret; }
static int ath6kl_init_upload(struct ath6kl *ar) { u32 param, options, sleep, address; int status = 0; if (ar->target_type != TARGET_TYPE_AR6003) return -EINVAL; /* temporarily disable system sleep */ address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; status = ath6kl_bmi_reg_read(ar, address, ¶m); if (status) return status; options = param; param |= ATH6KL_OPTION_SLEEP_DISABLE; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; status = ath6kl_bmi_reg_read(ar, address, ¶m); if (status) return status; sleep = param; param |= SM(SYSTEM_SLEEP_DISABLE, 1); status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; ath6kl_dbg(ATH6KL_DBG_TRC, "old options: %d, old sleep: %d\n", options, sleep); /* program analog PLL register */ status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER, 0xF9104001); if (status) return status; /* Run at 80/88MHz by default */ param = SM(CPU_CLOCK_STANDARD, 1); address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; param = 0; address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS; param = SM(LPO_CAL_ENABLE, 1); status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; /* WAR to avoid SDIO CRC err */ if (ar->version.target_ver == AR6003_REV2_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x20; address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN11_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN12_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; address = GPIO_BASE_ADDRESS + GPIO_PIN13_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; } /* write EEPROM data to Target RAM */ status = ath6kl_upload_board_file(ar); if (status) return status; /* transfer One time Programmable data */ status = ath6kl_upload_otp(ar); if (status) return status; /* Download Target firmware */ status = ath6kl_upload_firmware(ar); if (status) return status; status = ath6kl_upload_patch(ar); if (status) return status; /* Restore system sleep */ address = RTC_BASE_ADDRESS + SYSTEM_SLEEP_ADDRESS; status = ath6kl_bmi_reg_write(ar, address, sleep); if (status) return status; address = MBOX_BASE_ADDRESS + LOCAL_SCRATCH_ADDRESS; param = options | 0x20; status = ath6kl_bmi_reg_write(ar, address, param); if (status) return status; /* Configure GPIO AR6003 UART */ param = CONFIG_AR600x_DEBUG_UART_TX_PIN; status = ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_dbg_uart_txpin)), (u8 *)¶m, 4); return status; }
static int htc_issue_packets(struct htc_target *target, struct htc_endpoint *ep, struct list_head *pkt_queue) { int status = 0; u16 payload_len; struct sk_buff *skb; struct htc_frame_hdr *htc_hdr; struct htc_packet *packet; ath6kl_dbg(ATH6KL_DBG_HTC, "%s: queue: 0x%p, pkts %d\n", __func__, pkt_queue, get_queue_depth(pkt_queue)); while (!list_empty(pkt_queue)) { packet = list_first_entry(pkt_queue, struct htc_packet, list); list_del(&packet->list); skb = packet->skb; if (!skb) { WARN_ON_ONCE(1); status = -EINVAL; break; } payload_len = packet->act_len; /* setup HTC frame header */ htc_hdr = (struct htc_frame_hdr *) skb_push(skb, sizeof(*htc_hdr)); if (!htc_hdr) { WARN_ON_ONCE(1); status = -EINVAL; break; } packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; /* Endianess? */ put_unaligned((u16) payload_len, &htc_hdr->payld_len); htc_hdr->flags = packet->info.tx.flags; htc_hdr->eid = (u8) packet->endpoint; htc_hdr->ctrl[0] = 0; htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; spin_lock_bh(&target->tx_lock); /* store in look up queue to match completions */ list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue); ep->ep_st.tx_issued += 1; spin_unlock_bh(&target->tx_lock); status = ath6kl_hif_pipe_send(target->dev->ar, ep->pipe.pipeid_ul, NULL, skb); if (status != 0) { if (status != -ENOMEM) { /* TODO: if more than 1 endpoint maps to the * same PipeID, it is possible to run out of * resources in the HIF layer. * Don't emit the error */ ath6kl_dbg(ATH6KL_DBG_HTC, "%s: failed status:%d\n", __func__, status); } spin_lock_bh(&target->tx_lock); list_del(&packet->list); /* reclaim credits */ ep->cred_dist.credits += packet->info.tx.cred_used; spin_unlock_bh(&target->tx_lock); /* put it back into the callers queue */ list_add(&packet->list, pkt_queue); break; } } if (status != 0) { while (!list_empty(pkt_queue)) { if (status != -ENOMEM) { ath6kl_dbg(ATH6KL_DBG_HTC, "%s: failed pkt:0x%p status:%d\n", __func__, packet, status); } packet = list_first_entry(pkt_queue, struct htc_packet, list); list_del(&packet->list); packet->status = status; send_packet_completion(target, packet); } } return status; }