/* * Copyright (c) 2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* * Include definitions here that can be used to tune the WLAN module * behavior. Different customers can tune the behavior as per their needs, * here.
*/
/* * This configuration item enable/disable keepalive support. * Keepalive support: In the absence of any data traffic to AP, null * frames will be sent to the AP at periodic interval, to keep the association * active. This configuration item defines the periodic interval. * Use value of zero to disable keepalive support * Default: 60 seconds
*/ #define WLAN_CONFIG_KEEP_ALIVE_INTERVAL 60
/* * This configuration item sets the value of disconnect timeout * Firmware delays sending the disconnect event to the host for this * timeout after is gets disconnected from the current AP. * If the firmware successly roams within the disconnect timeout * it sends a new connect event
*/ #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10
/* Add cacheline space at front and back of buffer */
reserved = roundup((2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET + sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES, 4);
skb = dev_alloc_skb(size + reserved);
if (skb)
skb_reserve(skb, reserved - L1_CACHE_BYTES); return skb;
}
/* Fetch the address of the host_app_area_s
* instance in the host interest area */
address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
address = TARG_VTOP(ar->target_type, address);
if (ath6kl_diag_read32(ar, address, &data)) return -EIO;
/* connect to a service */ staticint ath6kl_connectservice(struct ath6kl *ar, struct htc_service_connect_req *con_req, char *desc)
{ int status; struct htc_service_connect_resp response;
memset(&response, 0, sizeof(response));
status = ath6kl_htc_conn_service(ar->htc_target, con_req, &response); if (status) {
ath6kl_err("failed to connect to %s service status:%d\n",
desc, status); return status;
}
switch (con_req->svc_id) { case WMI_CONTROL_SVC: if (test_bit(WMI_ENABLED, &ar->flag))
ath6kl_wmi_set_control_ep(ar->wmi, response.endpoint);
ar->ctrl_ep = response.endpoint; break; case WMI_DATA_BE_SVC:
set_ac2_ep_map(ar, WMM_AC_BE, response.endpoint); break; case WMI_DATA_BK_SVC:
set_ac2_ep_map(ar, WMM_AC_BK, response.endpoint); break; case WMI_DATA_VI_SVC:
set_ac2_ep_map(ar, WMM_AC_VI, response.endpoint); break; case WMI_DATA_VO_SVC:
set_ac2_ep_map(ar, WMM_AC_VO, response.endpoint); break; default:
ath6kl_err("service id is not mapped %d\n", con_req->svc_id); return -EINVAL;
}
/* these fields are the same for all service endpoints */
connect.ep_cb.tx_comp_multi = ath6kl_tx_complete;
connect.ep_cb.rx = ath6kl_rx;
connect.ep_cb.rx_refill = ath6kl_rx_refill;
connect.ep_cb.tx_full = ath6kl_tx_queue_full;
/* * Set the max queue depth so that our ath6kl_tx_queue_full handler * gets called.
*/
connect.max_txq_depth = MAX_DEFAULT_SEND_QUEUE_DEPTH;
connect.ep_cb.rx_refill_thresh = ATH6KL_MAX_RX_BUFFERS / 4; if (!connect.ep_cb.rx_refill_thresh)
connect.ep_cb.rx_refill_thresh++;
/* connect to control service */
connect.svc_id = WMI_CONTROL_SVC; if (ath6kl_connectservice(ar, &connect, "WMI CONTROL")) return -EIO;
connect.flags |= HTC_FLGS_TX_BNDL_PAD_EN;
/* * Limit the HTC message size on the send path, although e can * receive A-MSDU frames of 4K, we will only send ethernet-sized * (802.3) frames on the send path.
*/
connect.max_rxmsg_sz = WMI_MAX_TX_DATA_FRAME_LENGTH;
/* * To reduce the amount of committed memory for larger A_MSDU * frames, use the recv-alloc threshold mechanism for larger * packets.
*/
connect.ep_cb.rx_alloc_thresh = ATH6KL_BUFFER_SIZE;
connect.ep_cb.rx_allocthresh = ath6kl_alloc_amsdu_rxbuf;
/* * For the remaining data services set the connection flag to * reduce dribbling, if configured to do so.
*/
connect.conn_flags |= HTC_CONN_FLGS_REDUCE_CRED_DRIB;
connect.conn_flags &= ~HTC_CONN_FLGS_THRESH_MASK;
connect.conn_flags |= HTC_CONN_FLGS_THRESH_LVL_HALF;
connect.svc_id = WMI_DATA_BE_SVC;
if (ath6kl_connectservice(ar, &connect, "WMI DATA BE")) return -EIO;
/* connect to back-ground map this to WMI LOW_PRI */
connect.svc_id = WMI_DATA_BK_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) return -EIO;
/* connect to Video service, map this to HI PRI */
connect.svc_id = WMI_DATA_VI_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) return -EIO;
/* * Connect to VO service, this is currently not mapped to a WMI * priority stream due to historical reasons. WMI originally * defined 3 priorities over 3 mailboxes We can change this when * WMI is reworked so that priorities are not dependent on * mailboxes.
*/
connect.svc_id = WMI_DATA_VO_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VO")) return -EIO;
/* * Set HTC/Mbox operational parameters, this can only be called when the * target is in the BMI phase.
*/ staticint 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_hi32(ar, hi_mbox_io_block_sz, blk_size); if (status) {
ath6kl_err("bmi_write_memory for IO block size failed\n"); goto out;
}
if (mbox_isr_yield_val) { /* set the host interest area for the mbox ISR yield limit */
status = ath6kl_bmi_write_hi32(ar, hi_mbox_isr_yield_limit,
mbox_isr_yield_val); if (status) {
ath6kl_err("bmi_write_memory for yield limit failed\n"); goto out;
}
}
out: return status;
}
staticint ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx)
{ int ret;
/* * Configure the device for rx dot11 header rules. "0,0" are the * default values. Required if checksum offload is needed. Set * RxMetaVersion to 2.
*/
ret = ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx,
ar->rx_meta_ver, 0, 0); if (ret) {
ath6kl_err("unable to set the rx frame format: %d\n", ret); return ret;
}
if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) {
ret = ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1,
IGNORE_PS_FAIL_DURING_SCAN); if (ret) {
ath6kl_err("unable to set power save fail event policy: %d\n",
ret); return ret;
}
}
if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) {
ret = ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0,
WMI_FOLLOW_BARKER_IN_ERP); if (ret) {
ath6kl_err("unable to set barker preamble policy: %d\n",
ret); return ret;
}
}
ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx,
WLAN_CONFIG_KEEP_ALIVE_INTERVAL); if (ret) {
ath6kl_err("unable to set keep alive interval: %d\n", ret); return ret;
}
ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, idx,
WLAN_CONFIG_DISCONNECT_TIMEOUT); if (ret) {
ath6kl_err("unable to set disconnect timeout: %d\n", ret); return ret;
}
if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) {
ret = ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED); if (ret) {
ath6kl_err("unable to set txop bursting: %d\n", ret); return ret;
}
}
if (ar->p2p && (ar->vif_max == 1 || idx)) {
ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx,
P2P_FLAG_CAPABILITIES_REQ |
P2P_FLAG_MACADDR_REQ |
P2P_FLAG_HMODEL_REQ); if (ret) {
ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P capabilities (%d) - assuming P2P not supported\n",
ret);
ar->p2p = false;
}
}
if (ar->p2p && (ar->vif_max == 1 || idx)) { /* Enable Probe Request reporting for P2P */
ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); if (ret) {
ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe Request reporting (%d)\n",
ret);
}
}
return ret;
}
int ath6kl_configure_target(struct ath6kl *ar)
{
u32 param, ram_reserved_size;
u8 fw_iftype, fw_mode = 0, fw_submode = 0; int i, status;
param = !!(ar->conf_flags & ATH6KL_CONF_UART_DEBUG); if (ath6kl_bmi_write_hi32(ar, hi_serial_enable, param)) {
ath6kl_err("bmi_write_memory for uart debug failed\n"); return -EIO;
}
/* * Note: Even though the firmware interface type is * chosen as BSS_STA for all three interfaces, can * be configured to IBSS/AP as long as the fw submode * remains normal mode (0 - AP, STA and IBSS). But * due to an target assert in firmware only one interface is * configured for now.
*/
fw_iftype = HI_OPTION_FW_MODE_BSS_STA;
for (i = 0; i < ar->vif_max; i++)
fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS);
/* * Submodes when fw does not support dynamic interface * switching: * vif[0] - AP/STA/IBSS * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" * Otherwise, All the interface are initialized to p2p dev.
*/
if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
ar->fw_capabilities)) { for (i = 0; i < ar->vif_max; i++)
fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
(i * HI_OPTION_FW_SUBMODE_BITS);
} else { for (i = 0; i < ar->max_norm_iface; i++)
fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
(i * HI_OPTION_FW_SUBMODE_BITS);
for (i = ar->max_norm_iface; i < ar->vif_max; i++)
fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
(i * HI_OPTION_FW_SUBMODE_BITS);
if (ar->p2p && ar->vif_max == 1)
fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV;
}
if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest,
HTC_PROTOCOL_VERSION) != 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_hi32(ar, hi_option_flag, ¶m) != 0) {
ath6kl_err("bmi_read_memory for setting fwmode failed\n"); return -EIO;
}
/* * 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 (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) {
ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); return -EIO;
}
if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz,
ram_reserved_size) != 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;
/* Configure GPIO AR600x UART */
status = ath6kl_bmi_write_hi32(ar, hi_dbg_uart_txpin,
ar->hw.uarttx_pin); if (status) return status;
/* Only set the baud rate if we're actually doing debug */ if (ar->conf_flags & ATH6KL_CONF_UART_DEBUG) {
status = ath6kl_bmi_write_hi32(ar, hi_desired_baud_rate,
ar->hw.uarttx_rate); if (status) return status;
}
/* Configure target refclk_hz */ if (ar->hw.refclk_hz != 0) {
status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz,
ar->hw.refclk_hz); if (status) return status;
}
#ifdef CONFIG_OF /* * Check the device tree for a board-id and use it to construct * the pathname to the firmware file. Used (for now) to find a * fallback to the "bdata.bin" file--typically a symlink to the * appropriate board-specific file.
*/ staticbool check_device_tree(struct ath6kl *ar)
{ staticconstchar *board_id_prop = "atheros,board-id"; struct device_node *node; char board_filename[64]; constchar *board_id; int ret;
ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board,
&ar->fw_board_len); if (ret) {
ath6kl_err("Failed to get DT board file %s: %d\n",
board_filename, ret); continue;
}
of_node_put(node); returntrue;
} returnfalse;
} #else staticbool check_device_tree(struct ath6kl *ar)
{ returnfalse;
} #endif/* CONFIG_OF */
staticint ath6kl_fetch_board_file(struct ath6kl *ar)
{ constchar *filename; int ret;
if (ar->fw_board != NULL) return 0;
if (WARN_ON(ar->hw.fw_board == NULL)) return -EINVAL;
filename = ar->hw.fw_board;
ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
&ar->fw_board_len); if (ret == 0) { /* managed to get proper board file */ return 0;
}
if (check_device_tree(ar)) { /* got board file from device tree */ return 0;
}
/* there was no proper board file, try to use default instead */
ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
filename, ret);
filename = ar->hw.fw_default_board;
ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
&ar->fw_board_len); if (ret) {
ath6kl_err("Failed to get default board file %s: %d\n",
filename, ret); return ret;
}
ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n");
ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n");
return 0;
}
staticint ath6kl_fetch_otp_file(struct ath6kl *ar)
{ char filename[100]; int ret;
if (ar->fw_otp != NULL) return 0;
if (ar->hw.fw.otp == NULL) {
ath6kl_dbg(ATH6KL_DBG_BOOT, "no OTP file configured for this hw\n"); return 0;
}
if (WARN_ON(ar->fw_board == NULL)) return -ENOENT;
/* * Determine where in Target RAM to write Board Data. * For AR6004, host determine Target RAM address for * writing board data.
*/ if (ar->hw.board_addr != 0) {
board_address = ar->hw.board_addr;
ath6kl_bmi_write_hi32(ar, hi_board_data,
board_address);
} else {
ret = ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); if (ret) {
ath6kl_err("Failed to get board file target address.\n"); return ret;
}
}
/* determine where in target ram to write extended board data */
ret = ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); if (ret) {
ath6kl_err("Failed to get extended board file target address.\n"); return ret;
}
if (ar->target_type == TARGET_TYPE_AR6003 &&
board_ext_address == 0) {
ath6kl_err("Failed to get board file target address.\n"); return -EINVAL;
}
/* record the fact that Board Data IS initialized */ if ((ar->version.target_ver == AR6004_HW_1_3_VERSION) ||
(ar->version.target_ver == AR6004_HW_3_0_VERSION))
param = board_data_size; else
param = 1;
staticint ath6kl_init_upload(struct ath6kl *ar)
{
u32 param, options, sleep, address; int status = 0;
if (ar->target_type != TARGET_TYPE_AR6003 &&
ar->target_type != TARGET_TYPE_AR6004) 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 */ /* no need to control 40/44MHz clock on AR6004 */ if (ar->target_type != TARGET_TYPE_AR6004) {
status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
0xF9104001);
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;
}
staticint __ath6kl_init_hw_start(struct ath6kl *ar)
{ long timeleft; int ret, i; char buf[200];
ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n");
ret = ath6kl_hif_power_on(ar); if (ret) return ret;
ret = ath6kl_configure_target(ar); if (ret) goto err_power_off;
ret = ath6kl_init_upload(ar); if (ret) goto err_power_off;
/* Do we need to finish the BMI phase */
ret = ath6kl_bmi_done(ar); if (ret) goto err_power_off;
/* * 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.
*/
ret = ath6kl_htc_wait_target(ar->htc_target);
if (ret == -ETIMEDOUT) { /* * Most likely USB target is in odd state after reboot and * needs a reset. A cold reset makes the whole device * disappear from USB bus and initialisation starts from * beginning.
*/
ath6kl_warn("htc wait target timed out, resetting device\n");
ath6kl_init_hw_reset(ar); goto err_power_off;
} elseif (ret) {
ath6kl_err("htc wait target failed: %d\n", ret); goto err_power_off;
}
ret = ath6kl_init_service_ep(ar); if (ret) {
ath6kl_err("Endpoint service initialization failed: %d\n", ret); goto err_cleanup_scatter;
}
/* setup credit distribution */
ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info);
/* start HTC */
ret = ath6kl_htc_start(ar->htc_target); if (ret) { /* FIXME: call this */
ath6kl_cookie_cleanup(ar); goto err_cleanup_scatter;
}
/* Wait for Wmi event to be ready */
timeleft = wait_event_interruptible_timeout(ar->event_wq,
test_bit(WMI_READY,
&ar->flag),
WMI_TIMEOUT); if (timeleft <= 0) {
clear_bit(WMI_READY, &ar->flag);
ath6kl_err("wmi is not ready or wait was interrupted: %ld\n",
timeleft);
ret = -EIO; goto err_htc_stop;
}
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);
ret = -EIO; goto err_htc_stop;
}
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
/* communicate the wmi protocol verision to the target */ /* FIXME: return error */ if ((ath6kl_set_host_app_area(ar)) != 0)
ath6kl_err("unable to set the host app area\n");
for (i = 0; i < ar->vif_max; i++) {
ret = ath6kl_target_config_wlan_params(ar, i); if (ret) goto err_htc_stop;
}
if (ar->fw_recovery.enable)
timer_delete_sync(&ar->fw_recovery.hb_timer);
/* * After wmi_shudown all WMI events will be dropped. We * need to cleanup the buffers allocated in AP mode and * give disconnect notification to stack, which usually * happens in the disconnect_event. Simulate the disconnect * event by calling the function directly. Sometimes * disconnect_event will be received when the debug logs * are collected.
*/
ath6kl_wmi_shutdown(ar->wmi);
clear_bit(WMI_ENABLED, &ar->flag); if (ar->htc_target) {
ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__);
ath6kl_htc_stop(ar->htc_target);
}
/* * Try to reset the device if we can. The driver may have been * configure NOT to reset the target during a debug session.
*/
ath6kl_init_hw_reset(ar);
up(&ar->sem);
}
EXPORT_SYMBOL(ath6kl_stop_txrx);
Messung V0.5
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.