| //------------------------------------------------------------------------------ |
| // Copyright (c) 2004-2010 Atheros Communications Inc. |
| // All rights reserved. |
| // |
| // |
| // |
| // 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. |
| // |
| // |
| // |
| // Author(s): ="Atheros" |
| //------------------------------------------------------------------------------ |
| |
| #include "ar6000_drv.h" |
| #include "a_drv_api.h" |
| #include "ieee80211_ioctl.h" |
| #include "ar6kap_common.h" |
| #include "targaddrs.h" |
| #include "wlan_config.h" |
| |
| #ifdef BTCOEX |
| #include "a_hci.h" |
| #endif |
| |
| #ifdef WAC |
| #include "wac_defs.h" |
| #endif |
| |
| #ifdef P2P |
| #include "p2p_api.h" |
| #endif /* P2P */ |
| |
| extern int enablerssicompensation; |
| A_UINT32 tcmdRxFreq; |
| extern unsigned int wmitimeout; |
| extern int tspecCompliance; |
| extern int bmienable; |
| extern int bypasswmi; |
| #ifdef BTCOEX |
| extern int loghci; |
| #endif |
| static int |
| ar6000_ioctl_get_roam_tbl(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if(wmi_get_roam_tbl_cmd(arPriv->arWmi) != A_OK) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_get_roam_data(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| /* currently assume only roam times are required */ |
| if(wmi_get_roam_data_cmd(arPriv->arWmi, ROAM_DATA_TIME) != A_OK) { |
| return -EIO; |
| } |
| |
| |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_set_roam_ctrl(struct net_device *dev, char *userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_ROAM_CTRL_CMD cmd; |
| A_UINT8 size = sizeof(cmd); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, size)) { |
| return -EFAULT; |
| } |
| |
| if (cmd.roamCtrlType == WMI_SET_HOST_BIAS) { |
| if (cmd.info.bssBiasInfo.numBss > 1) { |
| size += (cmd.info.bssBiasInfo.numBss - 1) * sizeof(WMI_BSS_BIAS); |
| } |
| } |
| |
| if (copy_from_user(&cmd, userdata, size)) { |
| return -EFAULT; |
| } |
| |
| if(wmi_set_roam_ctrl_cmd(arPriv->arWmi, &cmd, size) != A_OK) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_set_powersave_timers(struct net_device *dev, char *userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_POWERSAVE_TIMERS_POLICY_CMD cmd; |
| A_UINT8 size = sizeof(cmd); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, size)) { |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&cmd, userdata, size)) { |
| return -EFAULT; |
| } |
| |
| if(wmi_set_powersave_timers_cmd(arPriv->arWmi, &cmd, size) != A_OK) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_set_qos_supp(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_QOS_SUPP_CMD cmd; |
| A_STATUS ret; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, (char *)((unsigned int*)rq->ifr_data + 1), |
| sizeof(cmd))) |
| { |
| return -EFAULT; |
| } |
| |
| ret = wmi_set_qos_supp_cmd(arPriv->arWmi, cmd.status); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| static int |
| ar6000_ioctl_get_wmm(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_to_user(rq->ifr_data, &arPriv->arWmmEnabled, sizeof(A_BOOL))) |
| return -EFAULT; |
| return 0; |
| } |
| |
| static int |
| ar6000_ioctl_set_wmm(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_WMM_CMD cmd; |
| A_STATUS ret; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, (char *)((unsigned int*)rq->ifr_data + 1), |
| sizeof(cmd))) |
| { |
| return -EFAULT; |
| } |
| |
| if (cmd.status == WMI_WMM_ENABLED) { |
| arPriv->arWmmEnabled = TRUE; |
| } else { |
| arPriv->arWmmEnabled = FALSE; |
| } |
| |
| ret = wmi_set_wmm_cmd(arPriv->arWmi, cmd.status); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| static int |
| ar6000_ioctl_set_txop(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_WMM_TXOP_CMD cmd; |
| A_STATUS ret; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, (char *)((unsigned int*)rq->ifr_data + 1), |
| sizeof(cmd))) |
| { |
| return -EFAULT; |
| } |
| |
| ret = wmi_set_wmm_txop(arPriv->arWmi, cmd.txopEnable); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| static int |
| ar6000_ioctl_get_rd(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| A_STATUS ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if(copy_to_user((char *)((unsigned int*)rq->ifr_data + 1), |
| &arPriv->arRegCode, sizeof(arPriv->arRegCode))) |
| ret = -EFAULT; |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_set_country(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_AP_SET_COUNTRY_CMD cmd; |
| A_STATUS ret; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, (char *)((unsigned int*)rq->ifr_data + 1), |
| sizeof(cmd))) |
| { |
| return -EFAULT; |
| } |
| |
| arPriv->ap_profile_flag = 1; /* There is a change in profile */ |
| |
| ret = wmi_set_country(arPriv->arWmi, cmd.countryCode); |
| A_MEMCPY(arPriv->arAp.ap_country_code, cmd.countryCode, 3); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| |
| /* Get power mode command */ |
| static int |
| ar6000_ioctl_get_power_mode(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_POWER_MODE_CMD power_mode; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| power_mode.powerMode = wmi_get_power_mode_cmd(arPriv->arWmi); |
| if (copy_to_user(rq->ifr_data, &power_mode, sizeof(WMI_POWER_MODE_CMD))) { |
| ret = -EFAULT; |
| } |
| |
| return ret; |
| } |
| |
| |
| static int |
| ar6000_ioctl_set_channelParams(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_CHANNEL_PARAMS_CMD cmd, *cmdp; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if( (arPriv->arNextMode == AP_NETWORK) && (cmd.numChannels || cmd.scanParam) ) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ERROR: Only wmode is allowed in AP mode\n")); |
| return -EIO; |
| } |
| |
| if (cmd.numChannels > 1) { |
| cmdp = A_MALLOC(130); |
| if (copy_from_user(cmdp, rq->ifr_data, |
| sizeof (*cmdp) + |
| ((cmd.numChannels - 1) * sizeof(A_UINT16)))) |
| { |
| A_FREE(cmdp); |
| return -EFAULT; |
| } |
| } else { |
| cmdp = &cmd; |
| } |
| |
| if ((arPriv->arPhyCapability == WMI_11NG_CAPABILITY) && |
| ((cmdp->phyMode == WMI_11A_MODE) || (cmdp->phyMode == WMI_11AG_MODE))) |
| { |
| ret = -EINVAL; |
| } |
| |
| if (!ret && |
| (wmi_set_channelParams_cmd(arPriv->arWmi, cmdp->scanParam, cmdp->phyMode, |
| cmdp->numChannels, cmdp->channelList) |
| != A_OK)) |
| { |
| ret = -EIO; |
| } |
| |
| if (!ret) |
| arPriv->phymode = cmdp->phyMode; |
| |
| if (cmd.numChannels > 1) { |
| A_FREE(cmdp); |
| } |
| |
| /* Set the profile change flag to allow a commit cmd */ |
| if (!ret) |
| arPriv->ap_profile_flag = 1; |
| |
| return ret; |
| } |
| |
| |
| static int |
| ar6000_ioctl_set_snr_threshold(struct net_device *dev, struct ifreq *rq) |
| { |
| |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SNR_THRESHOLD_PARAMS_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if( wmi_set_snr_threshold_params(arPriv->arWmi, &cmd) != A_OK ) { |
| ret = -EIO; |
| } |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_set_rssi_threshold(struct net_device *dev, struct ifreq *rq) |
| { |
| #define SWAP_THOLD(thold1, thold2) do { \ |
| USER_RSSI_THOLD tmpThold; \ |
| tmpThold.tag = thold1.tag; \ |
| tmpThold.rssi = thold1.rssi; \ |
| thold1.tag = thold2.tag; \ |
| thold1.rssi = thold2.rssi; \ |
| thold2.tag = tmpThold.tag; \ |
| thold2.rssi = tmpThold.rssi; \ |
| } while (0) |
| |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_RSSI_THRESHOLD_PARAMS_CMD cmd; |
| USER_RSSI_PARAMS rssiParams; |
| A_INT32 i, j; |
| int ret = 0; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user((char *)&rssiParams, (char *)((unsigned int *)rq->ifr_data + 1), sizeof(USER_RSSI_PARAMS))) { |
| return -EFAULT; |
| } |
| cmd.weight = rssiParams.weight; |
| cmd.pollTime = rssiParams.pollTime; |
| |
| A_MEMCPY(arSta->rssi_map, &rssiParams.tholds, sizeof(arSta->rssi_map)); |
| /* |
| * only 6 elements, so use bubble sorting, in ascending order |
| */ |
| for (i = 5; i > 0; i--) { |
| for (j = 0; j < i; j++) { /* above tholds */ |
| if (arSta->rssi_map[j+1].rssi < arSta->rssi_map[j].rssi) { |
| SWAP_THOLD(arSta->rssi_map[j+1], arSta->rssi_map[j]); |
| } else if (arSta->rssi_map[j+1].rssi == arSta->rssi_map[j].rssi) { |
| return EFAULT; |
| } |
| } |
| } |
| for (i = 11; i > 6; i--) { |
| for (j = 6; j < i; j++) { /* below tholds */ |
| if (arSta->rssi_map[j+1].rssi < arSta->rssi_map[j].rssi) { |
| SWAP_THOLD(arSta->rssi_map[j+1], arSta->rssi_map[j]); |
| } else if (arSta->rssi_map[j+1].rssi == arSta->rssi_map[j].rssi) { |
| return EFAULT; |
| } |
| } |
| } |
| |
| #ifdef DEBUG |
| for (i = 0; i < 12; i++) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("thold[%d].tag: %d, thold[%d].rssi: %d \n", |
| i, arSta->rssi_map[i].tag, i, arSta->rssi_map[i].rssi)); |
| } |
| #endif |
| |
| if (enablerssicompensation) { |
| for (i = 0; i < 6; i++) |
| arSta->rssi_map[i].rssi = rssi_compensation_reverse_calc(arPriv, arSta->rssi_map[i].rssi, TRUE); |
| for (i = 6; i < 12; i++) |
| arSta->rssi_map[i].rssi = rssi_compensation_reverse_calc(arPriv, arSta->rssi_map[i].rssi, FALSE); |
| } |
| |
| cmd.thresholdAbove1_Val = arSta->rssi_map[0].rssi; |
| cmd.thresholdAbove2_Val = arSta->rssi_map[1].rssi; |
| cmd.thresholdAbove3_Val = arSta->rssi_map[2].rssi; |
| cmd.thresholdAbove4_Val = arSta->rssi_map[3].rssi; |
| cmd.thresholdAbove5_Val = arSta->rssi_map[4].rssi; |
| cmd.thresholdAbove6_Val = arSta->rssi_map[5].rssi; |
| cmd.thresholdBelow1_Val = arSta->rssi_map[6].rssi; |
| cmd.thresholdBelow2_Val = arSta->rssi_map[7].rssi; |
| cmd.thresholdBelow3_Val = arSta->rssi_map[8].rssi; |
| cmd.thresholdBelow4_Val = arSta->rssi_map[9].rssi; |
| cmd.thresholdBelow5_Val = arSta->rssi_map[10].rssi; |
| cmd.thresholdBelow6_Val = arSta->rssi_map[11].rssi; |
| |
| if( wmi_set_rssi_threshold_params(arPriv->arWmi, &cmd) != A_OK ) { |
| ret = -EIO; |
| } |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_set_lq_threshold(struct net_device *dev, struct ifreq *rq) |
| { |
| |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_LQ_THRESHOLD_PARAMS_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, (char *)((unsigned int *)rq->ifr_data + 1), sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if( wmi_set_lq_threshold_params(arPriv->arWmi, &cmd) != A_OK ) { |
| ret = -EIO; |
| } |
| |
| return ret; |
| } |
| |
| |
| static int |
| ar6000_ioctl_set_probedSsid(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_PROBED_SSID_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_probedSsid_cmd(arPriv->arWmi, cmd.entryIndex, cmd.flag, cmd.ssidLength, |
| cmd.ssid) != A_OK) |
| { |
| ret = -EIO; |
| } |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_set_badAp(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_ADD_BAD_AP_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (cmd.badApIndex > WMI_MAX_BAD_AP_INDEX) { |
| return -EIO; |
| } |
| |
| if (A_MEMCMP(cmd.bssid, null_mac, AR6000_ETH_ADDR_LEN) == 0) { |
| /* |
| * This is a delete badAP. |
| */ |
| if (wmi_deleteBadAp_cmd(arPriv->arWmi, cmd.badApIndex) != A_OK) { |
| ret = -EIO; |
| } |
| } else { |
| if (wmi_addBadAp_cmd(arPriv->arWmi, cmd.badApIndex, cmd.bssid) != A_OK) { |
| ret = -EIO; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_create_qos(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_CREATE_PSTREAM_CMD cmd; |
| A_STATUS ret; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| ret = wmi_verify_tspec_params(&cmd, tspecCompliance); |
| if (ret == A_OK) |
| ret = wmi_create_pstream_cmd(arPriv->arWmi, &cmd); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| static int |
| ar6000_ioctl_delete_qos(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_DELETE_PSTREAM_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| ret = wmi_delete_pstream_cmd(arPriv->arWmi, cmd.trafficClass, cmd.tsid); |
| |
| switch (ret) { |
| case A_OK: |
| return 0; |
| case A_EBUSY : |
| return -EBUSY; |
| case A_NO_MEMORY: |
| return -ENOMEM; |
| case A_EINVAL: |
| default: |
| return -EFAULT; |
| } |
| } |
| |
| static int |
| ar6000_ioctl_get_qos_queue(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| struct ar6000_queuereq qreq; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if( copy_from_user(&qreq, rq->ifr_data, |
| sizeof(struct ar6000_queuereq))) |
| return -EFAULT; |
| |
| qreq.activeTsids = wmi_get_mapped_qos_queue(arPriv->arWmi, qreq.trafficClass); |
| |
| if (copy_to_user(rq->ifr_data, &qreq, |
| sizeof(struct ar6000_queuereq))) |
| { |
| ret = -EFAULT; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_HOST_TCMD_SUPPORT |
| static A_STATUS |
| ar6000_ioctl_tcmd_cmd_resp(struct net_device *dev, struct ifreq *rq, A_UINT8 *data, A_UINT32 len) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_UINT8 buf[4+TC_CMDS_SIZE_MAX]; |
| int ret = 0; |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| ar->tcmdRxReport = 0; |
| if (wmi_test_cmd(arPriv->arWmi, data, len) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, ar->tcmdRxReport != 0, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| *(A_UINT16*)&(buf[0]) = ar->tcmdResp.len; |
| buf[2] = ar->tcmdResp.ver; |
| A_MEMCPY((buf+4), ar->tcmdResp.buf, sizeof(ar->tcmdResp.buf)); |
| |
| if (!ret && copy_to_user(rq->ifr_data, buf, sizeof(buf))) { |
| ret = -EFAULT; |
| } |
| |
| up(&ar->arSem); |
| |
| return ret; |
| } |
| |
| static A_STATUS |
| ar6000_ioctl_tcmd_get_rx_report(struct net_device *dev, |
| struct ifreq *rq, A_UINT8 *data, A_UINT32 len) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_UINT32 buf[4+TCMD_MAX_RATES]; |
| int ret = 0; |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (ar->bIsDestroyProgress) { |
| up(&ar->arSem); |
| return -EBUSY; |
| } |
| |
| ar->tcmdRxReport = 0; |
| if (wmi_test_cmd(arPriv->arWmi, data, len) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, ar->tcmdRxReport != 0, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| buf[0] = ar->tcmdRxTotalPkt; |
| buf[1] = ar->tcmdRxRssi; |
| buf[2] = ar->tcmdRxcrcErrPkt; |
| buf[3] = ar->tcmdRxsecErrPkt; |
| A_MEMCPY(((A_UCHAR *)buf)+(4*sizeof(A_UINT32)), ar->tcmdRateCnt, sizeof(ar->tcmdRateCnt)); |
| A_MEMCPY(((A_UCHAR *)buf)+(4*sizeof(A_UINT32))+(TCMD_MAX_RATES *sizeof(A_UINT16)), ar->tcmdRateCntShortGuard, sizeof(ar->tcmdRateCntShortGuard)); |
| |
| if (!ret && copy_to_user(rq->ifr_data, buf, sizeof(buf))) { |
| ret = -EFAULT; |
| } |
| |
| up(&ar->arSem); |
| |
| return ret; |
| } |
| |
| void |
| ar6000_tcmd_rx_report_event(AR_SOFTC_DEV_T *arPriv, A_UINT8 * results, int len) |
| { |
| |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| TCMD_CONT_RX * rx_rep = (TCMD_CONT_RX *)results; |
| |
| if (TC_CMD_RESP == rx_rep->act) { |
| TC_CMDS *tCmd = (TC_CMDS *)results; |
| ar->tcmdResp.len = tCmd->hdr.u.parm.length; |
| ar->tcmdResp.ver = tCmd->hdr.u.parm.version; |
| A_MEMZERO(ar->tcmdResp.buf, sizeof(ar->tcmdResp.buf)); |
| A_MEMCPY(ar->tcmdResp.buf, tCmd->buf, sizeof(ar->tcmdResp.buf)); |
| ar->tcmdRxReport = 1; |
| } |
| else { /*(rx_rep->act == TCMD_CONT_RX_REPORT) */ |
| if (enablerssicompensation) { |
| rx_rep->u.report.rssiInDBm = rssi_compensation_calc_tcmd(ar, tcmdRxFreq, rx_rep->u.report.rssiInDBm,rx_rep->u.report.totalPkt); |
| } |
| |
| |
| ar->tcmdRxTotalPkt = rx_rep->u.report.totalPkt; |
| ar->tcmdRxRssi = rx_rep->u.report.rssiInDBm; |
| ar->tcmdRxcrcErrPkt = rx_rep->u.report.crcErrPkt; |
| ar->tcmdRxsecErrPkt = rx_rep->u.report.secErrPkt; |
| ar->tcmdRxReport = 1; |
| A_MEMZERO(ar->tcmdRateCnt, sizeof(ar->tcmdRateCnt)); |
| A_MEMZERO(ar->tcmdRateCntShortGuard, sizeof(ar->tcmdRateCntShortGuard)); |
| A_MEMCPY(ar->tcmdRateCnt, rx_rep->u.report.rateCnt, sizeof(ar->tcmdRateCnt)); |
| A_MEMCPY(ar->tcmdRateCntShortGuard, rx_rep->u.report.rateCntShortGuard, sizeof(ar->tcmdRateCntShortGuard)); |
| } |
| |
| wake_up(&arPriv->arEvent); |
| } |
| #endif /* CONFIG_HOST_TCMD_SUPPORT*/ |
| |
| static int |
| ar6000_ioctl_set_error_report_bitmask(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_TARGET_ERROR_REPORT_BITMASK cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| ret = wmi_set_error_report_bitmask(arPriv->arWmi, cmd.bitmask); |
| |
| return (ret==0 ? ret : -EINVAL); |
| } |
| |
| static int |
| ar6000_clear_target_stats(struct net_device *dev) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| TARGET_STATS *pStats = &arPriv->arTargetStats; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| AR6000_SPIN_LOCK(&arPriv->arPrivLock, 0); |
| A_MEMZERO(pStats, sizeof(TARGET_STATS)); |
| AR6000_SPIN_UNLOCK(&arPriv->arPrivLock, 0); |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_get_target_stats(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| TARGET_STATS_CMD *pcmd; |
| TARGET_STATS *pStats = &arPriv->arTargetStats; |
| int ret = 0; |
| |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| pcmd = (TARGET_STATS_CMD *) A_MALLOC(sizeof(TARGET_STATS_CMD)); |
| |
| if (pcmd == NULL) { |
| return -ENOMEM; |
| } |
| if (copy_from_user(pcmd, rq->ifr_data, sizeof(*pcmd))) { |
| A_FREE(pcmd); |
| return -EFAULT; |
| } |
| if (down_interruptible(&ar->arSem)) { |
| A_FREE(pcmd); |
| return -ERESTARTSYS; |
| } |
| if (ar->bIsDestroyProgress) { |
| up(&ar->arSem); |
| A_FREE(pcmd); |
| return -EBUSY; |
| } |
| |
| arPriv->statsUpdatePending = TRUE; |
| |
| if(wmi_get_stats_cmd(arPriv->arWmi) != A_OK) { |
| up(&ar->arSem); |
| A_FREE(pcmd); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->statsUpdatePending == FALSE, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| if (!ret && copy_to_user(rq->ifr_data, pStats, sizeof(*pStats))) { |
| ret = -EFAULT; |
| } |
| |
| if (pcmd->clearStats == 1) { |
| ret = ar6000_clear_target_stats(dev); |
| } |
| |
| up(&ar->arSem); |
| |
| a_meminfo_report(FALSE); |
| A_FREE(pcmd); |
| |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_get_ap_stats(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| A_UINT32 action; /* Allocating only the desired space on the frame. Declaring is as a WMI_AP_MODE_STAT variable results in exceeding the compiler imposed limit on the maximum frame size */ |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_PER_STA_STAT *pStats = ar->arAPStats; |
| WMI_AP_MODE_STAT *pret_stat; |
| A_UINT8 i, j=0; |
| |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&action, (char *)((unsigned int*)rq->ifr_data + 1), |
| sizeof(A_UINT32))) |
| { |
| return -EFAULT; |
| } |
| if (action == AP_CLEAR_STATS) { |
| AR6000_SPIN_LOCK(&ar->arLock, 0); |
| for(i = 0; i < NUM_CONN; i++) { |
| if(ar->connTbl[i].arPriv == arPriv) { |
| pStats[i].tx_bytes = 0; |
| pStats[i].tx_pkts = 0; |
| pStats[i].tx_error = 0; |
| pStats[i].tx_discard = 0; |
| pStats[i].rx_bytes = 0; |
| pStats[i].rx_pkts = 0; |
| pStats[i].rx_error = 0; |
| pStats[i].rx_discard = 0; |
| } |
| } |
| AR6000_SPIN_UNLOCK(&ar->arLock, 0); |
| return ret; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| arPriv->statsUpdatePending = TRUE; |
| |
| if(wmi_get_stats_cmd(arPriv->arWmi) != A_OK) { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->statsUpdatePending == FALSE, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| pret_stat = (WMI_AP_MODE_STAT *) A_MALLOC(sizeof(WMI_AP_MODE_STAT)); |
| if (pret_stat == NULL) { |
| return -ENOMEM; |
| } |
| A_MEMZERO(pret_stat, sizeof(*pret_stat)); |
| for(i = 0; i < NUM_CONN; i++) { |
| if(ar->connTbl[i].arPriv == arPriv) { |
| pret_stat->sta[j].aid = pStats[i].aid; |
| pret_stat->sta[j].tx_bytes = pStats[i].tx_bytes; |
| pret_stat->sta[j].tx_pkts = pStats[i].tx_pkts; |
| pret_stat->sta[j].tx_error = pStats[i].tx_error; |
| pret_stat->sta[j].tx_discard = pStats[i].tx_discard; |
| pret_stat->sta[j].rx_bytes = pStats[i].rx_bytes; |
| pret_stat->sta[j].rx_pkts = pStats[i].rx_pkts; |
| pret_stat->sta[j].rx_error = pStats[i].rx_error; |
| pret_stat->sta[j].rx_discard = pStats[i].rx_discard; |
| j++; |
| } |
| } |
| |
| if (!ret && copy_to_user(rq->ifr_data, pret_stat, sizeof(*pret_stat))) { |
| ret = -EFAULT; |
| } |
| |
| up(&ar->arSem); |
| A_FREE(pret_stat); |
| return ret; |
| } |
| |
| static int |
| ar6000_ioctl_set_access_params(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_ACCESS_PARAMS_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_access_params_cmd(arPriv->arWmi, cmd.ac, cmd.txop, cmd.eCWmin, cmd.eCWmax, |
| cmd.aifsn) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| ar6000_ioctl_set_disconnect_timeout(struct net_device *dev, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_DISC_TIMEOUT_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_disctimeout_cmd(arPriv->arWmi, cmd.disconnectTimeout) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_voice_pkt_size(struct net_device *dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_VOICE_PKT_SIZE_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_voice_pkt_size_cmd(arPriv->arWmi, cmd.voicePktSize) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| |
| return (ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_max_sp_len(struct net_device *dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_MAX_SP_LEN_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_max_sp_len_cmd(arPriv->arWmi, cmd.maxSPLen) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return (ret); |
| } |
| |
| #ifdef BTCOEX |
| static int |
| ar6000_xioctl_set_bt_status_cmd(struct net_device *dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BT_STATUS_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_bt_status_cmd(arPriv->arWmi, cmd.streamType, cmd.status) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_bt_params_cmd(struct net_device *dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BT_PARAMS_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_bt_params_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_fe_ant_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_FE_ANT_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_fe_ant_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_colocated_bt_dev_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_colocated_bt_dev_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_btinquiry_page_config_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_btinquiry_page_config_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_sco_config_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_SCO_CONFIG_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_sco_config_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_a2dp_config_cmd(struct net_device * dev, |
| char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_A2DP_CONFIG_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_a2dp_config_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_aclcoex_config_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_aclcoex_config_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar60000_xioctl_set_btcoex_debug_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_DEBUG_CMD cmd; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_btcoex_debug_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_set_btcoex_bt_operating_status_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMD cmd; |
| int ret = 0; |
| A_UINT32 btProfileType; |
| A_UINT32 btOperatingStatus; |
| |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| btProfileType = cmd.btProfileType; |
| btOperatingStatus = cmd.btOperatingStatus; |
| |
| if (BT_STREAM_SCAN != btProfileType && |
| BT_STATUS_ON == btOperatingStatus && |
| arPriv->arNetworkType == AP_NETWORK) |
| { |
| switch (ar->delbaState) |
| { |
| case REASON_DELBA_INIT: |
| ar6000_send_delba (ar, REASON_TEAR_DOWN); |
| ar->delbaState = REASON_TEAR_DOWN; |
| break; |
| case REASON_TEAR_DOWN: |
| break; |
| case REASON_DELBA_TIMEOUT: |
| // |
| // Uninitialize timer, and keep the state as TEAR_DOWN for rearm the timer |
| // |
| ar->IsdelbaTimerInitialized = FALSE; |
| A_UNTIMEOUT (&ar->delbaTimer); |
| ar->delbaState = REASON_TEAR_DOWN; |
| break; |
| } |
| |
| } |
| |
| if (wmi_set_btcoex_bt_operating_status_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| |
| if (BT_STREAM_SCAN != btProfileType && |
| BT_STATUS_OFF == btOperatingStatus && |
| arPriv->arNetworkType == AP_NETWORK) |
| { |
| switch (ar->delbaState) |
| { |
| case REASON_DELBA_INIT: |
| break; |
| case REASON_TEAR_DOWN: |
| ar->delbaState = REASON_DELBA_TIMEOUT; |
| |
| // Initiate A_TIMER |
| ar->IsdelbaTimerInitialized = TRUE; |
| A_TIMEOUT_MS (&ar->delbaTimer, DELBA_TIMEOUT, 0); |
| break; |
| |
| case REASON_DELBA_TIMEOUT: |
| break; |
| } |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| ar6000_xioctl_get_btcoex_config_cmd(struct net_device * dev, char * userdata, |
| struct ifreq *rq) |
| { |
| |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR6000_BTCOEX_CONFIG btcoexConfig; |
| WMI_BTCOEX_CONFIG_EVENT *pbtcoexConfigEv = &arPriv->arBtcoexConfig; |
| |
| int ret = 0; |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| if (copy_from_user(&btcoexConfig.configCmd, userdata, sizeof(AR6000_BTCOEX_CONFIG))) { |
| return -EFAULT; |
| } |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (wmi_get_btcoex_config_cmd(arPriv->arWmi, (WMI_GET_BTCOEX_CONFIG_CMD *)&btcoexConfig.configCmd) != A_OK) |
| { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| arPriv->statsUpdatePending = TRUE; |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->statsUpdatePending == FALSE, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| if (!ret && copy_to_user(btcoexConfig.configEvent, pbtcoexConfigEv, sizeof(WMI_BTCOEX_CONFIG_EVENT))) { |
| ret = -EFAULT; |
| } |
| up(&ar->arSem); |
| return ret; |
| } |
| |
| static int |
| ar6000_xioctl_get_btcoex_stats_cmd(struct net_device * dev, char * userdata, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR6000_BTCOEX_STATS btcoexStats; |
| WMI_BTCOEX_STATS_EVENT *pbtcoexStats = &arPriv->arBtcoexStats; |
| int ret = 0; |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (copy_from_user(&btcoexStats.statsEvent, userdata, sizeof(AR6000_BTCOEX_CONFIG))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_get_btcoex_stats_cmd(arPriv->arWmi) != A_OK) |
| { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| arPriv->statsUpdatePending = TRUE; |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->statsUpdatePending == FALSE, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| if (!ret && copy_to_user(btcoexStats.statsEvent, pbtcoexStats, sizeof(WMI_BTCOEX_STATS_EVENT))) { |
| ret = -EFAULT; |
| } |
| |
| |
| up(&ar->arSem); |
| |
| return(ret); |
| } |
| #endif |
| |
| static int |
| ar6000_xioctl_set_excess_tx_retry_thres_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_SET_EXCESS_TX_RETRY_THRES_CMD cmd; |
| int ret = 0; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_excess_tx_retry_thres_cmd(arPriv->arWmi, &cmd) != A_OK) |
| { |
| ret = -EINVAL; |
| } |
| return(ret); |
| } |
| |
| #ifdef WAC |
| static int |
| ar6000_xioctl_wac_ctrl_req_get_cmd(struct net_device * dev, char * userdata, struct ifreq *rq) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_WAC_CTRL_REQ_CMD cmd; |
| int ret = 0; |
| |
| if (ar->bIsDestroyProgress) { |
| return -EBUSY; |
| } |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (down_interruptible(&ar->arSem)) { |
| return -ERESTARTSYS; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(WMI_WAC_CTRL_REQ_CMD))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_wac_ctrl_req_cmd(arPriv->arWmi, &cmd) != A_OK) |
| { |
| up(&ar->arSem); |
| return -EIO; |
| } |
| |
| arPriv->statsUpdatePending = TRUE; |
| |
| wait_event_interruptible_timeout(arPriv->arEvent, arPriv->statsUpdatePending == FALSE, wmitimeout * HZ); |
| |
| if (signal_pending(current)) { |
| ret = -EINTR; |
| } |
| |
| if (!ret && copy_to_user(rq->ifr_data, &arPriv->wacInfo, sizeof(WMI_GET_WAC_INFO))) { |
| ret = -EFAULT; |
| } |
| |
| up(&ar->arSem); |
| |
| return(ret); |
| } |
| #endif |
| |
| static int |
| ar6000_xioctl_set_passphrase_cmd(struct net_device * dev, char * userdata) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| WMI_SET_PASSPHRASE_CMD cmd; |
| int ret = 0; |
| |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| |
| if (copy_from_user(&cmd, userdata, sizeof(cmd))) { |
| return -EFAULT; |
| } |
| |
| if (wmi_set_passphrase_cmd(arPriv->arWmi, &cmd) == A_OK) |
| { |
| /* enable WPA offload */ |
| arSta->arConnectCtrlFlags |= CONNECT_DO_WPA_OFFLOAD; |
| ret = 0; |
| } else { |
| ret = -EINVAL; |
| } |
| return(ret); |
| } |
| |
| #ifdef CONFIG_HOST_GPIO_SUPPORT |
| struct ar6000_gpio_intr_wait_cmd_s gpio_intr_results; |
| /* gpio_reg_results and gpio_data_available are protected by arSem */ |
| static struct ar6000_gpio_register_cmd_s gpio_reg_results; |
| static A_BOOL gpio_data_available; /* Requested GPIO data available */ |
| static A_BOOL gpio_intr_available; /* GPIO interrupt info available */ |
| static A_BOOL gpio_ack_received; /* GPIO ack was received */ |
| |
| /* Host-side initialization for General Purpose I/O support */ |
| void ar6000_gpio_init(void) |
| { |
| gpio_intr_available = FALSE; |
| gpio_data_available = FALSE; |
| gpio_ack_received = FALSE; |
| } |
| |
| /* |
| * Called when a GPIO interrupt is received from the Target. |
| * intr_values shows which GPIO pins have interrupted. |
| * input_values shows a recent value of GPIO pins. |
| */ |
| void |
| ar6000_gpio_intr_rx(AR_SOFTC_DEV_T *arPriv, A_UINT32 intr_mask, A_UINT32 input_values) |
| { |
| gpio_intr_results.intr_mask = intr_mask; |
| gpio_intr_results.input_values = input_values; |
| *((volatile A_BOOL *)&gpio_intr_available) = TRUE; |
| wake_up(&arPriv->arEvent); |
| } |
| |
| /* |
| * This is called when a response is received from the Target |
| * for a previous or ar6000_gpio_input_get or ar6000_gpio_register_get |
| * call. |
| */ |
| void |
| ar6000_gpio_data_rx(AR_SOFTC_DEV_T *arPriv, A_UINT32 reg_id, A_UINT32 value) |
| { |
| gpio_reg_results.gpioreg_id = reg_id; |
| gpio_reg_results.value = value; |
| *((volatile A_BOOL *)&gpio_data_available) = TRUE; |
| wake_up(&arPriv->arEvent); |
| } |
| |
| /* |
| * This is called when an acknowledgement is received from the Target |
| * for a previous or ar6000_gpio_output_set or ar6000_gpio_register_set |
| * call. |
| */ |
| void |
| ar6000_gpio_ack_rx(AR_SOFTC_DEV_T *arPriv) |
| { |
| gpio_ack_received = TRUE; |
| wake_up(&arPriv->arEvent); |
| } |
| |
| A_STATUS |
| ar6000_gpio_output_set(struct net_device *dev, |
| A_UINT32 set_mask, |
| A_UINT32 clear_mask, |
| A_UINT32 enable_mask, |
| A_UINT32 disable_mask) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| gpio_ack_received = FALSE; |
| return wmi_gpio_output_set(arPriv->arWmi, |
| set_mask, clear_mask, enable_mask, disable_mask); |
| } |
| |
| static A_STATUS |
| ar6000_gpio_input_get(struct net_device *dev) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| *((volatile A_BOOL *)&gpio_data_available) = FALSE; |
| return wmi_gpio_input_get(arPriv->arWmi); |
| } |
| |
| static A_STATUS |
| ar6000_gpio_register_set(struct net_device *dev, |
| A_UINT32 gpioreg_id, |
| A_UINT32 value) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| gpio_ack_received = FALSE; |
| return wmi_gpio_register_set(arPriv->arWmi, gpioreg_id, value); |
| } |
| |
| static A_STATUS |
| ar6000_gpio_register_get(struct net_device *dev, |
| A_UINT32 gpioreg_id) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| *((volatile A_BOOL *)&gpio_data_available) = FALSE; |
| return wmi_gpio_register_get(arPriv->arWmi, gpioreg_id); |
| } |
| |
| static A_STATUS |
| ar6000_gpio_intr_ack(struct net_device *dev, |
| A_UINT32 ack_mask) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| gpio_intr_available = FALSE; |
| return wmi_gpio_intr_ack(arPriv->arWmi, ack_mask); |
| } |
| #endif /* CONFIG_HOST_GPIO_SUPPORT */ |
| |
| #if defined(CONFIG_TARGET_PROFILE_SUPPORT) |
| static struct prof_count_s prof_count_results; |
| static A_BOOL prof_count_available; /* Requested GPIO data available */ |
| |
| static A_STATUS |
| prof_count_get(struct net_device *dev) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| |
| *((volatile A_BOOL *)&prof_count_available) = FALSE; |
| return wmi_prof_count_get_cmd(arPriv->arWmi); |
| } |
| |
| /* |
| * This is called when a response is received from the Target |
| * for a previous prof_count_get call. |
| */ |
| void |
| prof_count_rx(A_UINT32 addr, A_UINT32 count) |
| { |
| prof_count_results.addr = addr; |
| prof_count_results.count = count; |
| *((volatile A_BOOL *)&prof_count_available) = TRUE; |
| wake_up(&arEvent); |
| } |
| #endif /* CONFIG_TARGET_PROFILE_SUPPORT */ |
| |
| #ifdef BTCOEX |
| static A_STATUS |
| ar6000_create_acl_data_osbuf(struct net_device *dev, A_UINT8 *userdata, void **p_osbuf) |
| { |
| void *osbuf = NULL; |
| A_UINT8 tmp_space[8]; |
| HCI_ACL_DATA_PKT *acl; |
| A_UINT8 hdr_size, *datap=NULL; |
| A_STATUS ret = A_OK; |
| |
| /* ACL is in data path. There is a need to create pool |
| * mechanism for allocating and freeing NETBUFs - ToDo later. |
| */ |
| |
| *p_osbuf = NULL; |
| acl = (HCI_ACL_DATA_PKT *)tmp_space; |
| hdr_size = sizeof(acl->hdl_and_flags) + sizeof(acl->data_len); |
| |
| do { |
| if (a_copy_from_user(acl, userdata, hdr_size)) { |
| ret = A_EFAULT; |
| break; |
| } |
| |
| osbuf = A_NETBUF_ALLOC(hdr_size + acl->data_len); |
| if (osbuf == NULL) { |
| ret = A_NO_MEMORY; |
| break; |
| } |
| A_NETBUF_PUT(osbuf, hdr_size + acl->data_len); |
| datap = (A_UINT8 *)A_NETBUF_DATA(osbuf); |
| |
| /* Real copy to osbuf */ |
| acl = (HCI_ACL_DATA_PKT *)(datap); |
| A_MEMCPY(acl, tmp_space, hdr_size); |
| if (a_copy_from_user(acl->data, userdata + hdr_size, acl->data_len)) { |
| ret = A_EFAULT; |
| break; |
| } |
| } while(FALSE); |
| |
| if (ret == A_OK) { |
| *p_osbuf = osbuf; |
| } else { |
| A_NETBUF_FREE(osbuf); |
| } |
| return ret; |
| } |
| #endif |
| |
| int |
| ar6000_ioctl_ap_setparam(AR_SOFTC_DEV_T *arPriv, int param, int value) |
| { |
| int ret=0; |
| |
| switch(param) { |
| case IEEE80211_PARAM_WPA: |
| switch (value) { |
| case WPA_MODE_WPA1: |
| arPriv->arAuthMode = WMI_WPA_AUTH; |
| break; |
| case WPA_MODE_WPA2: |
| arPriv->arAuthMode = WMI_WPA2_AUTH; |
| break; |
| case WPA_MODE_AUTO: |
| arPriv->arAuthMode = WMI_WPA_AUTH | WMI_WPA2_AUTH; |
| break; |
| case WPA_MODE_NONE: |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_AUTHMODE: |
| if(value == IEEE80211_AUTH_WPA_PSK) { |
| if (WMI_WPA_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA_PSK_AUTH; |
| } else if (WMI_WPA2_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA2_PSK_AUTH; |
| } else if ((WMI_WPA_AUTH | WMI_WPA2_AUTH) == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA_PSK_AUTH | WMI_WPA2_PSK_AUTH; |
| } else { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Error - Setting PSK "\ |
| "mode when WPA param was set to %d\n", |
| arPriv->arAuthMode)); |
| ret = -EIO; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_UCASTCIPHER: |
| arPriv->arPairwiseCrypto = 0; |
| if(value & (1<<IEEE80211_CIPHER_AES_CCM)) { |
| arPriv->arPairwiseCrypto |= AES_CRYPT; |
| } |
| if(value & (1<<IEEE80211_CIPHER_TKIP)) { |
| arPriv->arPairwiseCrypto |= TKIP_CRYPT; |
| } |
| if(!arPriv->arPairwiseCrypto) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| ("Error - Invalid cipher in WPA \n")); |
| ret = -EIO; |
| } |
| break; |
| case IEEE80211_PARAM_PRIVACY: |
| if(value == 0) { |
| arPriv->arDot11AuthMode = OPEN_AUTH; |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arPairwiseCryptoLen = 0; |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| arPriv->arGroupCryptoLen = 0; |
| } |
| break; |
| #ifdef WAPI_ENABLE |
| case IEEE80211_PARAM_WAPI: |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("WAPI Policy: %d\n", value)); |
| arPriv->arDot11AuthMode = OPEN_AUTH; |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| if(value & 0x1) { |
| arPriv->arPairwiseCrypto = WAPI_CRYPT; |
| arPriv->arGroupCrypto = WAPI_CRYPT; |
| } else { |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| } |
| break; |
| #endif |
| } |
| return ret; |
| } |
| |
| int |
| ar6000_ioctl_setparam(AR_SOFTC_DEV_T *arPriv, int param, int value) |
| { |
| A_BOOL profChanged = FALSE; |
| int ret=0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if(arPriv->arNextMode == AP_NETWORK) { |
| arPriv->ap_profile_flag = 1; /* There is a change in profile */ |
| switch (param) { |
| case IEEE80211_PARAM_WPA: |
| case IEEE80211_PARAM_AUTHMODE: |
| case IEEE80211_PARAM_UCASTCIPHER: |
| case IEEE80211_PARAM_PRIVACY: |
| case IEEE80211_PARAM_WAPI: |
| ret = ar6000_ioctl_ap_setparam(arPriv, param, value); |
| return ret; |
| } |
| } |
| |
| switch (param) { |
| case IEEE80211_PARAM_WPA: |
| switch (value) { |
| case WPA_MODE_WPA1: |
| arPriv->arAuthMode = WMI_WPA_AUTH; |
| profChanged = TRUE; |
| break; |
| case WPA_MODE_WPA2: |
| arPriv->arAuthMode = WMI_WPA2_AUTH; |
| profChanged = TRUE; |
| break; |
| case WPA_MODE_NONE: |
| arPriv->arAuthMode = WMI_NONE_AUTH; |
| profChanged = TRUE; |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_AUTHMODE: |
| switch(value) { |
| case IEEE80211_AUTH_WPA_PSK: |
| if (WMI_WPA_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA_PSK_AUTH; |
| profChanged = TRUE; |
| } else if (WMI_WPA2_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA2_PSK_AUTH; |
| profChanged = TRUE; |
| } else { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Error - Setting PSK "\ |
| "mode when WPA param was set to %d\n", |
| arPriv->arAuthMode)); |
| ret = -EIO; |
| } |
| break; |
| case IEEE80211_AUTH_WPA_CCKM: |
| if (WMI_WPA2_AUTH == arPriv->arAuthMode) { |
| arPriv->arAuthMode = WMI_WPA2_AUTH_CCKM; |
| } else { |
| arPriv->arAuthMode = WMI_WPA_AUTH_CCKM; |
| } |
| break; |
| default: |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_UCASTCIPHER: |
| switch (value) { |
| case IEEE80211_CIPHER_AES_CCM: |
| arPriv->arPairwiseCrypto = AES_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_TKIP: |
| arPriv->arPairwiseCrypto = TKIP_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_WEP: |
| arPriv->arPairwiseCrypto = WEP_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_NONE: |
| arPriv->arPairwiseCrypto = NONE_CRYPT; |
| profChanged = TRUE; |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_UCASTKEYLEN: |
| if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) { |
| ret = -EIO; |
| } else { |
| arPriv->arPairwiseCryptoLen = value; |
| } |
| break; |
| case IEEE80211_PARAM_MCASTCIPHER: |
| switch (value) { |
| case IEEE80211_CIPHER_AES_CCM: |
| arPriv->arGroupCrypto = AES_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_TKIP: |
| arPriv->arGroupCrypto = TKIP_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_WEP: |
| arPriv->arGroupCrypto = WEP_CRYPT; |
| profChanged = TRUE; |
| break; |
| case IEEE80211_CIPHER_NONE: |
| arPriv->arGroupCrypto = NONE_CRYPT; |
| profChanged = TRUE; |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_MCASTKEYLEN: |
| if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) { |
| ret = -EIO; |
| } else { |
| arPriv->arGroupCryptoLen = value; |
| } |
| break; |
| case IEEE80211_PARAM_COUNTERMEASURES: |
| if (ar->arWmiReady == FALSE) { |
| return -EIO; |
| } |
| wmi_set_tkip_countermeasures_cmd(arPriv->arWmi, value); |
| break; |
| default: |
| break; |
| } |
| if ((arPriv->arNextMode != AP_NETWORK) && (profChanged == TRUE)) { |
| /* |
| * profile has changed. Erase ssid to signal change |
| */ |
| A_MEMZERO(arPriv->arSsid, sizeof(arPriv->arSsid)); |
| } |
| |
| return ret; |
| } |
| |
| int |
| ar6000_sendkey(AR_SOFTC_DEV_T *arPriv, struct ieee80211req_key *ik, KEY_USAGE keyUsage) |
| { |
| A_STATUS status; |
| CRYPTO_TYPE keyType = NONE_CRYPT; |
| |
| switch (ik->ik_type) { |
| case IEEE80211_CIPHER_WEP: |
| keyType = WEP_CRYPT; |
| break; |
| case IEEE80211_CIPHER_TKIP: |
| keyType = TKIP_CRYPT; |
| break; |
| case IEEE80211_CIPHER_AES_CCM: |
| keyType = AES_CRYPT; |
| break; |
| default: |
| break; |
| } |
| |
| if (IEEE80211_CIPHER_CCKM_KRK != ik->ik_type) { |
| if (NONE_CRYPT == keyType) { |
| return A_ERROR; |
| } |
| |
| if ((WEP_CRYPT == keyType)&&(!arPriv->arConnected)) { |
| int index = ik->ik_keyix; |
| |
| if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(ik->ik_keylen)) { |
| return A_ERROR; |
| } |
| |
| A_MEMZERO(arPriv->arWepKeyList[index].arKey, |
| sizeof(arPriv->arWepKeyList[index].arKey)); |
| A_MEMCPY(arPriv->arWepKeyList[index].arKey, ik->ik_keydata, ik->ik_keylen); |
| arPriv->arWepKeyList[index].arKeyLen = ik->ik_keylen; |
| |
| if(ik->ik_flags & IEEE80211_KEY_DEFAULT){ |
| arPriv->arDefTxKeyIndex = index; |
| } |
| |
| return A_OK; |
| } |
| |
| status = wmi_addKey_cmd(arPriv->arWmi, ik->ik_keyix, keyType, keyUsage, |
| ik->ik_keylen, (A_UINT8 *)&ik->ik_keyrsc, |
| ik->ik_keydata, KEY_OP_INIT_VAL, ik->ik_macaddr, |
| SYNC_BOTH_WMIFLAG); |
| |
| } else { |
| status = wmi_add_krk_cmd(arPriv->arWmi, ik->ik_keydata); |
| } |
| |
| return status; |
| } |
| |
| int |
| ar6000_ioctl_setkey(AR_SOFTC_DEV_T *arPriv, struct ieee80211req_key *ik) |
| { |
| KEY_USAGE keyUsage; |
| A_STATUS status; |
| CRYPTO_TYPE keyType = NONE_CRYPT; |
| |
| if ( (0 == memcmp(ik->ik_macaddr, null_mac, IEEE80211_ADDR_LEN)) || |
| (0 == memcmp(ik->ik_macaddr, bcast_mac, IEEE80211_ADDR_LEN)) ) { |
| keyUsage = GROUP_USAGE; |
| } else { |
| keyUsage = PAIRWISE_USAGE; |
| } |
| |
| if(arPriv->arNextMode == AP_NETWORK) { |
| AR_SOFTC_AP_T *arAp = &arPriv->arAp; |
| |
| if (keyUsage == GROUP_USAGE) { |
| A_MEMCPY(&arAp->ap_mode_bkey, ik, sizeof(struct ieee80211req_key)); |
| } |
| |
| #ifdef WAPI_ENABLE |
| if(arPriv->arPairwiseCrypto == WAPI_CRYPT) { |
| return ap_set_wapi_key(arPriv, ik); |
| } |
| #endif |
| |
| status = ar6000_sendkey(arPriv, ik, keyUsage); |
| |
| } else { |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| |
| #ifdef USER_KEYS |
| arSta->user_saved_keys.keyOk = FALSE; |
| arSta->user_saved_keys.keyType = keyType; |
| if (keyUsage == GROUP_USAGE) { |
| A_MEMCPY(&arSta->user_saved_keys.bcast_ik, ik, |
| sizeof(struct ieee80211req_key)); |
| } else { |
| A_MEMCPY(&arSta->user_saved_keys.ucast_ik, ik, |
| sizeof(struct ieee80211req_key)); |
| } |
| #endif |
| |
| if (((WMI_WPA_PSK_AUTH == arPriv->arAuthMode) || |
| (WMI_WPA2_PSK_AUTH == arPriv->arAuthMode)) && |
| (GROUP_USAGE & keyUsage)) |
| { |
| A_UNTIMEOUT(&arSta->disconnect_timer); |
| } |
| |
| status = ar6000_sendkey(arPriv, ik, keyUsage); |
| |
| #ifdef USER_KEYS |
| if (status == A_OK) { |
| arSta->user_saved_keys.keyOk = TRUE; |
| } |
| #endif |
| } |
| |
| if (status != A_OK) { |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| int ar6000_xioctl_add_wowptn(AR_SOFTC_DEV_T *arPriv, char *userdata) |
| { |
| #define WOW_PATTERN_SIZE 64 |
| #define WOW_MASK_SIZE 64 |
| WMI_ADD_WOW_PATTERN_CMD cmd; |
| AR_SOFTC_T *ar=arPriv->arSoftc; |
| int ret = 0; |
| A_UINT8 *pmask_data; |
| A_UINT8 *ppattern_data; |
| pmask_data = (A_UINT8 *) A_MALLOC(WOW_MASK_SIZE * sizeof(A_UINT8)); |
| if (pmask_data == NULL) { |
| return -ENOMEM; |
| } |
| ppattern_data = (A_UINT8 *) A_MALLOC(WOW_PATTERN_SIZE * sizeof(A_UINT8)); |
| if (ppattern_data == NULL) { |
| A_FREE(pmask_data); |
| return -ENOMEM; |
| } |
| A_MEMZERO(pmask_data, WOW_MASK_SIZE * sizeof(A_UINT8)); |
| A_MEMZERO(ppattern_data, WOW_PATTERN_SIZE * sizeof(A_UINT8)); |
| |
| do { |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| break; |
| } |
| if(copy_from_user(&cmd, userdata, |
| sizeof(WMI_ADD_WOW_PATTERN_CMD))) |
| { |
| ret = -EFAULT; |
| break; |
| } |
| if (copy_from_user(ppattern_data, |
| userdata + 3, |
| cmd.filter_size)) |
| { |
| ret = -EFAULT; |
| break; |
| } |
| if (copy_from_user(pmask_data, |
| (userdata + 3 + cmd.filter_size), |
| cmd.filter_size)) |
| { |
| ret = -EFAULT; |
| break; |
| } |
| if (wmi_add_wow_pattern_cmd(arPriv->arWmi, |
| &cmd, ppattern_data, pmask_data, |
| cmd.filter_size) != A_OK) |
| { |
| ret = -EIO; |
| } |
| } while(FALSE); |
| A_FREE(pmask_data); |
| A_FREE(ppattern_data); |
| return ret; |
| #undef WOW_PATTERN_SIZE |
| #undef WOW_MASK_SIZE |
| } |
| |
| int ar6000_xioctl_dump_htccredit(AR_SOFTC_DEV_T *arPriv) |
| { |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| int ret = 0; |
| if (ar->arHtcTarget != NULL) { |
| #ifdef ATH_DEBUG_MODULE |
| HTCDumpCreditStates(ar->arHtcTarget); |
| #endif /* ATH_DEBUG_MODULE */ |
| #ifdef HTC_EP_STAT_PROFILING |
| { |
| HTC_ENDPOINT_STATS *pstats; |
| int i; |
| pstats = (HTC_ENDPOINT_STATS *) A_MALLOC(sizeof(HTC_ENDPOINT_STATS)); |
| if (pstats == NULL) { |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < 5; i++) { |
| if (HTCGetEndpointStatistics(ar->arHtcTarget, |
| i, |
| HTC_EP_STAT_SAMPLE_AND_CLEAR, |
| pstats)) { |
| A_PRINTF(KERN_ALERT"------- Profiling Endpoint : %d \n", i); |
| A_PRINTF(KERN_ALERT"TxCreditLowIndications : %d \n", pstats->TxCreditLowIndications); |
| A_PRINTF(KERN_ALERT"TxIssued : %d \n", pstats->TxIssued); |
| A_PRINTF(KERN_ALERT"TxDropped: %d \n", pstats->TxDropped); |
| A_PRINTF(KERN_ALERT"TxPacketsBundled : %d \n", pstats->TxPacketsBundled); |
| A_PRINTF(KERN_ALERT"TxBundles : %d \n", pstats->TxBundles); |
| A_PRINTF(KERN_ALERT"TxCreditRpts : %d \n", pstats->TxCreditRpts); |
| A_PRINTF(KERN_ALERT"TxCreditsRptsFromRx : %d \n", pstats->TxCreditRptsFromRx); |
| A_PRINTF(KERN_ALERT"TxCreditsRptsFromOther : %d \n", pstats->TxCreditRptsFromOther); |
| A_PRINTF(KERN_ALERT"TxCreditsRptsFromEp0 : %d \n", pstats->TxCreditRptsFromEp0); |
| A_PRINTF(KERN_ALERT"TxCreditsFromRx : %d \n", pstats->TxCreditsFromRx); |
| A_PRINTF(KERN_ALERT"TxCreditsFromOther : %d \n", pstats->TxCreditsFromOther); |
| A_PRINTF(KERN_ALERT"TxCreditsFromEp0 : %d \n", pstats->TxCreditsFromEp0); |
| A_PRINTF(KERN_ALERT"TxCreditsConsummed : %d \n", pstats->TxCreditsConsummed); |
| A_PRINTF(KERN_ALERT"TxCreditsReturned : %d \n", pstats->TxCreditsReturned); |
| A_PRINTF(KERN_ALERT"RxReceived : %d \n", pstats->RxReceived); |
| A_PRINTF(KERN_ALERT"RxPacketsBundled : %d \n", pstats->RxPacketsBundled); |
| A_PRINTF(KERN_ALERT"RxLookAheads : %d \n", pstats->RxLookAheads); |
| A_PRINTF(KERN_ALERT"RxBundleLookAheads : %d \n", pstats->RxBundleLookAheads); |
| A_PRINTF(KERN_ALERT"RxBundleIndFromHdr : %d \n", pstats->RxBundleIndFromHdr); |
| A_PRINTF(KERN_ALERT"RxAllocThreshHit : %d \n", pstats->RxAllocThreshHit); |
| A_PRINTF(KERN_ALERT"RxAllocThreshBytes : %d \n", pstats->RxAllocThreshBytes); |
| A_PRINTF(KERN_ALERT"---- \n"); |
| } |
| } |
| A_FREE(pstats); |
| |
| } |
| #endif |
| } |
| return ret; |
| } |
| |
| int ar6000_ioctl_get_wpaie(AR_SOFTC_DEV_T *arPriv, char *userdata) |
| { |
| struct ieee80211req_wpaie *pwpaie; |
| int ret = 0; |
| AR_SOFTC_T *ar=arPriv->arSoftc; |
| |
| pwpaie = (struct ieee80211req_wpaie *) A_MALLOC(sizeof(struct ieee80211req_wpaie)); |
| if (pwpaie == NULL) { |
| return -ENOMEM; |
| } |
| |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(pwpaie, userdata, sizeof(*pwpaie))) { |
| ret = -EFAULT; |
| } else if (ar6000_ap_mode_get_wpa_ie(arPriv, pwpaie)) { |
| ret = -EFAULT; |
| } else if(copy_to_user(userdata, pwpaie, sizeof(*pwpaie))) { |
| ret = -EFAULT; |
| } |
| A_FREE(pwpaie); |
| return ret; |
| |
| } |
| |
| #ifdef BTCOEX |
| int ar6000_xioctl_hci_cmd(AR_SOFTC_DEV_T *arPriv, char *userdata) |
| { |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| char tmp_buf[512]; |
| A_INT8 i; |
| WMI_HCI_CMD *cmd = (WMI_HCI_CMD *)tmp_buf; |
| A_UINT8 size; |
| int ret = 0; |
| |
| size = sizeof(cmd->cmd_buf_sz); |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(cmd, userdata, size)) { |
| ret = -EFAULT; |
| } else if(copy_from_user(cmd->buf, userdata + size, cmd->cmd_buf_sz)) { |
| ret = -EFAULT; |
| } else { |
| if (wmi_send_hci_cmd(arPriv->arWmi, cmd->buf, cmd->cmd_buf_sz) != A_OK) { |
| ret = -EIO; |
| } else if(loghci) { |
| A_PRINTF_LOG("HCI Command To PAL --> \n"); |
| for(i = 0; i < cmd->cmd_buf_sz; i++) { |
| A_PRINTF_LOG("0x%02x ",cmd->buf[i]); |
| if((i % 10) == 0) { |
| A_PRINTF_LOG("\n"); |
| } |
| } |
| A_PRINTF_LOG("\n"); |
| A_PRINTF_LOG("==================================\n"); |
| } |
| } |
| return ret; |
| } |
| #endif |
| |
| int ar6000_xioctl_fetch_targ_regs(AR_SOFTC_DEV_T *arPriv, struct ifreq *rq, HIF_DEVICE *hifDevice) |
| { |
| int ret = 0; |
| AR_SOFTC_T *ar=arPriv->arSoftc; |
| A_UINT32 targregs[AR6003_FETCH_TARG_REGS_COUNT]; |
| |
| if (ar->arTargetType == TARGET_TYPE_AR6003) { |
| ar6k_FetchTargetRegs(hifDevice, targregs); |
| if (copy_to_user((A_UINT32 *)rq->ifr_data, &targregs, sizeof(targregs))) |
| { |
| ret = -EFAULT; |
| } |
| } else { |
| ret = -EOPNOTSUPP; |
| } |
| return ret; |
| |
| } |
| |
| #ifdef P2P |
| int ar6000_xioctl_wmi_p2p_peer(AR_SOFTC_DEV_T *arPriv, char *userdata, struct ifreq *rq) |
| { |
| A_UINT8 buf[12]; |
| const A_UINT8 zero_mac[] = {0,0,0,0,0,0}; |
| |
| A_UINT8 peer_info_buf[900]; |
| A_UINT32 peer_info_buf_used; |
| |
| int first_element; |
| int ret = 0; |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(buf, userdata, 12)) { |
| ret = -EFAULT; |
| } else { |
| /* |
| * Check the "next" value set in driver_ar6003.c of the supplicant. |
| * This determines whether we have a "P2P_PEER FIRST" (if = 1) or |
| * "P2P_PEER NEXT-<addr>" (if = 2) command, or just a plain p2p_peer <addr> |
| * command (if = 0) |
| */ |
| if (buf[6] != 0) { |
| if (buf[6] == 1) { |
| first_element = 1; |
| } else { |
| first_element = 0; |
| } |
| peer_info_buf_used = p2p_get_next_addr(A_WMI_GET_P2P_CTX(arPriv), |
| buf, peer_info_buf, |
| sizeof(peer_info_buf), |
| first_element); |
| if (peer_info_buf_used == 0) { |
| ret = -ENODEV; |
| } |
| *((A_UINT32 *)rq->ifr_data) = peer_info_buf_used; |
| if(copy_to_user(((A_UINT32 *)(rq->ifr_data)+1), |
| peer_info_buf, peer_info_buf_used)) { |
| ret = -EFAULT; |
| } |
| } else { |
| if (p2p_peer(A_WMI_GET_P2P_CTX(arPriv), |
| buf, *(buf+6)) == A_OK) { |
| peer_info_buf_used = p2p_get_peer_info( A_WMI_GET_P2P_CTX(arPriv), buf, |
| peer_info_buf, |
| sizeof(peer_info_buf)); |
| *((A_UINT32 *)rq->ifr_data) = peer_info_buf_used; |
| if(copy_to_user(((A_UINT32 *)(rq->ifr_data)+1), |
| peer_info_buf, peer_info_buf_used)) { |
| ret = -EFAULT; |
| } |
| } else { |
| if(copy_to_user((A_UINT16 *)rq->ifr_data, |
| zero_mac, IEEE80211_ADDR_LEN)) { |
| ret = -EFAULT; |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| int ar6000_xioctl_wmi_p2p_provdisc(AR_SOFTC_DEV_T *arPriv, char *userdata) |
| { |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| A_UINT8 peer[IEEE80211_ADDR_LEN]; |
| A_UINT16 wps_method; |
| A_UINT8 buf[8]; |
| int ret = 0; |
| |
| A_MEMZERO(peer, IEEE80211_ADDR_LEN); |
| |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(buf, userdata, 8)) { |
| ret = -EFAULT; |
| } else { |
| A_MEMCPY(peer, buf, IEEE80211_ADDR_LEN); |
| wps_method = (*(A_UINT16 *)(&buf[6])); |
| |
| if (p2p_prov_disc_req(A_WMI_GET_P2P_CTX(arPriv), |
| peer, wps_method) != A_OK) { |
| ret = -EFAULT; |
| } |
| } |
| return ret; |
| } |
| |
| int ar6000_xioctl_wmi_p2p_set(AR_SOFTC_DEV_T *arPriv, char *userdata) |
| { |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| int ret = 0; |
| WMI_P2P_SET_CMD set_p2p_config; |
| A_MEMZERO(&set_p2p_config, sizeof(WMI_P2P_SET_CMD)); |
| |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(&set_p2p_config, userdata, |
| sizeof(WMI_P2P_SET_CMD))) { |
| ret = -EFAULT; |
| } else { |
| if (set_p2p_config.config_id == WMI_P2P_CONFID_CROSS_CONNECT) { |
| p2p_set_group_capability(A_WMI_GET_P2P_CTX(arPriv), |
| P2P_GROUP_CAPAB_CROSS_CONN, |
| set_p2p_config.val.cross_conn.flag); |
| } else if(set_p2p_config.config_id == WMI_P2P_CONFID_CONCURRENT_MODE) { |
| p2p_set_device_capability(A_WMI_GET_P2P_CTX(arPriv), |
| P2P_DEV_CAPAB_CONCURRENT_OPER, |
| set_p2p_config.val.concurrent_mode.flag); |
| } |
| |
| wmi_p2p_set_cmd(arPriv->arWmi, &set_p2p_config); |
| } |
| return ret; |
| |
| } |
| |
| int ar6000_xioctl_wmi_p2p_sdpdtx(AR_SOFTC_DEV_T *arPriv, char *userdata, struct ifreq *rq) |
| { |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| WMI_P2P_SDPD_TX_CMD *psdpd_tx_cmd; |
| A_UINT32 qid = 0; |
| int ret = 0; |
| psdpd_tx_cmd = (WMI_P2P_SDPD_TX_CMD *) A_MALLOC(sizeof(WMI_P2P_SDPD_TX_CMD)); |
| if (psdpd_tx_cmd == NULL) { |
| return -ENOMEM; |
| } |
| A_MEMZERO(psdpd_tx_cmd, sizeof(WMI_P2P_SDPD_TX_CMD)); |
| |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(psdpd_tx_cmd, userdata, |
| sizeof(WMI_P2P_SDPD_TX_CMD))) { |
| ret = -EFAULT; |
| } else { |
| if (p2p_sdpd_tx_cmd(A_WMI_GET_P2P_CTX(arPriv), |
| psdpd_tx_cmd, &qid) != |
| A_OK) { |
| ret = -EFAULT; |
| } else { |
| if(copy_to_user((A_UINT8 *)rq->ifr_data, |
| (void *)&qid, sizeof(A_UINT32))) { |
| ret = -EFAULT; |
| } |
| } |
| } |
| A_FREE(psdpd_tx_cmd); |
| return ret; |
| } |
| |
| #endif /* P2P */ |
| |
| int ar6000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) |
| { |
| AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); |
| AR_SOFTC_T *ar = arPriv->arSoftc; |
| AR_SOFTC_STA_T *arSta = &arPriv->arSta; |
| AR_SOFTC_AP_T *arAp = &arPriv->arAp; |
| HIF_DEVICE *hifDevice = ar->arHifDevice; |
| int ret = 0, param; |
| unsigned int address = 0; |
| unsigned int length = 0; |
| unsigned char *buffer; |
| char *userdata; |
| |
| /* |
| * ioctl operations may have to wait for the Target, so we cannot hold rtnl. |
| * Prevent the device from disappearing under us and release the lock during |
| * the ioctl operation. |
| */ |
| dev_hold(dev); |
| rtnl_unlock(); |
| |
| if (cmd == AR6000_IOCTL_EXTENDED) { |
| /* |
| * This allows for many more wireless ioctls than would otherwise |
| * be available. Applications embed the actual ioctl command in |
| * the first word of the parameter block, and use the command |
| * AR6000_IOCTL_EXTENDED_CMD on the ioctl call. |
| */ |
| get_user(cmd, (int *)rq->ifr_data); |
| userdata = (char *)(((unsigned int *)rq->ifr_data)+1); |
| if(is_xioctl_allowed(arPriv->arNextMode, |
| arPriv->arNetworkSubType, cmd) != A_OK) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("xioctl: cmd=%d not allowed in this mode\n",cmd)); |
| ret = -EOPNOTSUPP; |
| goto ioctl_done; |
| } |
| } else { |
| A_STATUS ret = is_iwioctl_allowed(arPriv->arNextMode, cmd); |
| if(ret == A_ENOTSUP) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("iwioctl: cmd=0x%x not allowed in this mode\n", cmd)); |
| ret = -EOPNOTSUPP; |
| goto ioctl_done; |
| } else if (ret == A_ERROR) { |
| /* It is not our ioctl (out of range ioctl) */ |
| ret = -EOPNOTSUPP; |
| goto ioctl_done; |
| } |
| userdata = (char *)rq->ifr_data; |
| } |
| |
| if ((ar->arWlanState == WLAN_DISABLED) && |
| ((cmd != AR6000_XIOCTRL_WMI_SET_WLAN_STATE) && |
| (cmd != AR6000_XIOCTL_GET_WLAN_SLEEP_STATE) && |
| (cmd != AR6000_XIOCTL_DIAG_READ) && |
| (cmd != AR6000_XIOCTL_DIAG_WRITE) && |
| (cmd != AR6000_XIOCTL_SET_BT_HW_POWER_STATE) && |
| (cmd != AR6000_XIOCTL_GET_BT_HW_POWER_STATE) && |
| (cmd != AR6000_IOCTL_WMI_GETREV) && |
| (cmd != AR6000_XIOCTL_RESUME_DRIVER))) |
| { |
| ret = -EIO; |
| goto ioctl_done; |
| } |
| |
| |
| ret = 0; |
| switch(cmd) |
| { |
| case IEEE80211_IOCTL_SETPARAM: |
| { |
| int param, value; |
| int *ptr = (int *)rq->ifr_ifru.ifru_newname; |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else { |
| param = *ptr++; |
| value = *ptr; |
| ret = ar6000_ioctl_setparam(arPriv,param,value); |
| } |
| break; |
| } |
| case IEEE80211_IOCTL_SETKEY: |
| { |
| struct ieee80211req_key keydata; |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(&keydata, userdata, |
| sizeof(struct ieee80211req_key))) { |
| ret = -EFAULT; |
| } else { |
| ar6000_ioctl_setkey(arPriv, &keydata); |
| } |
| break; |
| } |
| case IEEE80211_IOCTL_DELKEY: |
| case IEEE80211_IOCTL_SETOPTIE: |
| { |
| //ret = -EIO; |
| break; |
| } |
| case IEEE80211_IOCTL_SETMLME: |
| { |
| struct ieee80211req_mlme mlme; |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(&mlme, userdata, |
| sizeof(struct ieee80211req_mlme))) { |
| ret = -EFAULT; |
| } else { |
| switch (mlme.im_op) { |
| case IEEE80211_MLME_AUTHORIZE: |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("setmlme AUTHORIZE %02X:%02X\n", |
| mlme.im_macaddr[4], mlme.im_macaddr[5])); |
| break; |
| case IEEE80211_MLME_UNAUTHORIZE: |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("setmlme UNAUTHORIZE %02X:%02X\n", |
| mlme.im_macaddr[4], mlme.im_macaddr[5])); |
| break; |
| case IEEE80211_MLME_DEAUTH: |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("setmlme DEAUTH %02X:%02X\n", |
| mlme.im_macaddr[4], mlme.im_macaddr[5])); |
| //remove_sta(ar, mlme.im_macaddr); |
| break; |
| case IEEE80211_MLME_DISASSOC: |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("setmlme DISASSOC %02X:%02X\n", |
| mlme.im_macaddr[4], mlme.im_macaddr[5])); |
| //remove_sta(ar, mlme.im_macaddr); |
| break; |
| default: |
| ret = 0; |
| goto ioctl_done; |
| } |
| |
| wmi_ap_set_mlme(arPriv->arWmi, mlme.im_op, mlme.im_macaddr, |
| mlme.im_reason); |
| } |
| break; |
| } |
| case IEEE80211_IOCTL_ADDPMKID: |
| { |
| struct ieee80211req_addpmkid req; |
| if (ar->arWmiReady == FALSE) { |
| ret = -EIO; |
| } else if (copy_from_user(&req, userdata, sizeof(struct ieee80211req_addpmkid))) { |
| ret = -EFAULT; |
| } else { |
| A_STATUS status; |
| |
| AR_DEBUG_PRINTF(ATH_DEBUG_WLAN_CONNECT,("Add pmkid for %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x en=%d\n", |
| req.pi_bssid[0], req.pi_bssid[1], req.pi_bssid[2], |
| req.pi_bssid[3], req.pi_bssid[4], req.pi_bssid[5], |
| req.pi_enable)); |
| |
| status = wmi_setPmkid_cmd(arPriv->arWmi, req.pi_bssid, req.pi_pmkid, |
| req.pi_enable); |
| |
| if (status != A_OK) { |
| ret = -EIO; |
| goto ioctl_done; |
| } |
| } |
| break; |
| } |
| #ifdef CONFIG_HOST_TCMD_SUPPORT |
| case AR6000_XIOCTL_TCMD_CONT_TX: |
| { |
| TCMD_CONT_TX txCmd; |
| |
| if ((ar->tcmdPm == TCMD_PM_SLEEP) || |
| (ar->tcmdPm == TCMD_PM_DEEPSLEEP)) |
| { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Can NOT send tx tcmd when target is asleep! \n")); |
| ret = -EOPNOTSUPP; |
| goto ioctl_done; |
| } |
| |
| if(copy_from_user(&txCmd, userdata, sizeof(TCMD_CONT_TX))) { |
| ret = -EFAULT; |
| goto ioctl_done; |
| } else { |
| wmi_test_cmd(arPriv->arWmi,(A_UINT8 *)&txCmd, sizeof(TCMD_CONT_TX)); |
| } |
| } |
| break; |
| case AR6000_XIOCTL_TCMD_CONT_RX: |
| { |
| TCMD_CONT_RX rxCmd; |
| |
| if ((ar->tcmdPm == TCMD_PM_SLEEP) || |
| (ar->tcmdPm == TCMD_PM_DEEPSLEEP)) |
| { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Can NOT send rx tcmd when target is asleep! \n")); |
| ret = -EOPNOTSUPP; |
| goto ioctl_done; |
| } |
| if(copy_from_user(&rxCmd, userdata, sizeof(TCMD_CONT_RX))) { |
| ret = -EFAULT; |
| goto ioctl_done; |
| } |
| |
| switch(rxCmd.act) |
| { |
| case TCMD_CONT_RX_PROMIS: |
| case TCMD_CONT_RX_FILTER: |
| case TCMD_CONT_RX_SETMAC: |
| case TCMD_CONT_RX_SET_ANT_SWITCH_TABLE: |
| wmi_test_cmd(arPriv->arWmi,(A_UINT8 *)&rxCmd, |
| sizeof(TCMD_CONT_RX)); |
| tcmdRxFreq = rxCmd.u.para.freq; |
| break; |
| case TCMD_CONT_RX_REPORT: |
| ar6000_ioctl_tcmd_get_rx_report(dev, rq, |
| (A_UINT8 *)&rxCmd, sizeof(TCMD_CONT_RX)); |
| break; |
| default: |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Unknown Cont Rx mode: %d\n",rxCmd.act)); |
| ret = -EINVAL; |
| goto ioctl_done; |
| } |
| } |
| break; |
| case AR6000_XIOCTL_TCMD_PM: |
| { |
| TCMD_PM pmCmd; |
| |
| if(copy_from_user(&pmCmd, userdata, sizeof(TCMD_PM))) { |
| ret = -EFAULT; |
| goto ioctl_done; |
| } |
| ar->tcmdPm = pmCmd.mode; |
| wmi_test_cmd(arPriv->arWmi, (A_UINT8*)&pmCmd, sizeof(TCMD_PM)); |
| } |
| break; |
| |
| case AR6000_XIOCTL_TCMD_CMDS: |
| { |
| TC_CMDS cmdsCmd; |
| if(copy_from_user(&cmdsCmd, userdata, sizeof(TC_CMDS))) { |
| ret = -EFAULT; |
| goto ioctl_done; |
| } |
| ar6000_ioctl_tcmd_cmd_resp(dev, rq, (A_UINT8 *)&cmdsCmd, sizeof(TC_CMDS)); |
| #if 0 |
| wmi_test_cmd(arPriv->arWmi, (A_UINT8*)&cmdsCmd, sizeof(TC_CMDS)); |
| #endif |
| } |
| break; |
| |
| case AR6000_XIOCTL_TCMD_SETREG: |
| { |
| TCMD_SET_REG setRegCmd; |
| |
| if(copy_from_user(&setRegCmd, userdata, sizeof(TCMD_SET_REG))) { |
| ret = -EFAULT; |
| goto ioctl_done; |
| } |
| wmi_test_cmd(arPriv->arWmi, (A_UINT8*)&setRegCmd, sizeof(TCMD_SET_REG)); |
| } |
| break; |
| #endif /* CONFIG_HOST_TCMD_SUPPORT */ |
| |
| case AR6000_XIOCTL_BMI_DONE: |
| if(bmienable) |
| { |
| rtnl_lock(); /* ar6000_init expects to be called holding rtnl lock */ |
| ret = ar6000_init(dev); |
| rtnl_unlock(); |
| } |
| else |
| { |
| ret = BMIDone(hifDevice); |
| } |
| break; |
| |
| case AR6000_XIOCTL_BMI_READ_MEMORY: |
| get_user(address, (unsigned int *)userdata); |
| get_user(length, (unsigned int *)userdata + 1); |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("Read Memory (address: 0x%x, length: %d)\n", |
| address, length)); |
| if ((buffer = (unsigned char *)A_MALLOC(length)) != NULL) { |
| A_MEMZERO(buffer, length); |
| ret = BMIReadMemory(hifDevice, address, buffer, length); |
| if (copy_to_user(rq->ifr_data, buffer, length)) { |
| ret = -EFAULT; |
| } |
| A_FREE(buffer); |
| } else { |
| ret = -ENOMEM; |
| } |
| break; |
| |
| case AR6000_XIOCTL_BMI_WRITE_MEMORY: |
| get_user(address, (unsigned int *)userdata); |
| get_user(length, (unsigned int *)userdata + 1); |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("Write Memory (address: 0x%x, length: %d)\n", |
| address, length)); |
| if ((buffer = (unsigned char *)A_MALLOC(length)) != NULL) { |
| A_MEMZERO(buffer, length); |
| if (copy_from_user(buffer, &userdata[sizeof(address) + |
| sizeof(length)], length)) |
| { |
| ret = -EFAULT; |
| } else { |
| ret = BMIWriteMemory(hifDevice, address, buffer, length); |
| } |
| A_FREE(buffer); |
| } else { |
| ret = -ENOMEM; |
| } |
| break; |
| |
| case AR6000_XIOCTL_BMI_TEST: |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("No longer supported\n")); |
| ret = -EOPNOTSUPP; |
| break; |
| |
| case AR6000_XIOCTL_BMI_EXECUTE: |
| get_user(address, (unsigned int *)userdata); |
| get_user(param, (unsigned int *)userdata + 1); |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("Execute (address: 0x%x, param: %d)\n", |
| address, param)); |
| ret = BMIExecute(hifDevice, address, (A_UINT32*)¶m); |
| put_user(param, (unsigned int *)rq->ifr_data); /* return value */ |
| break; |
| |
| case AR6000_XIOCTL_BMI_SET_APP_START: |
| get_user(address, (unsigned int *)userdata); |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("Set App Start (address: 0x%x)\n", address)); |
| ret = BMISetAppStart(hifDevice, address); |
| break; |
| |
| case AR6000_XIOCTL_BMI_READ_SOC_REGISTER: |
| get_user(address, (unsigned int *)userdata); |
| ret = BMIReadSOCRegister(hifDevice, address, (A_UINT32*)¶m); |
| put_user(param, (unsigned int *)rq->ifr_data); /* return value */ |
| break; |
| |
| case AR6000_XIOCTL_BMI_WRITE_SOC_REGISTER: |
| get_user(address, (unsigned int *)userdata); |
| get_user(param, (unsigned int *)userdata + 1); |
| ret = BMIWriteSOCRegister(hifDevice, address, param); |
| break; |
| |
| #ifdef HTC_RAW_INTERFACE |
| case AR6000_XIOCTL_HTC_RAW_OPEN: |
| ret = A_OK; |
| if (!arRawIfEnabled(ar)) { |
| /* make sure block size is set in case the target was reset since last |
| * BMI phase (i.e. flashup downloads) */ |
| ret = ar6000_set_htc_params(ar->arHifDevice, |
| ar->arTargetType, |
| 0, /* use default yield */ |
| 0 /* use default number of HTC ctrl buffers */ |
| ); |
| if (A_FAILED(ret)) { |
| break; |
| } |
| /* Terminate the BMI phase */ |
| ret = BMIDone(hifDevice); |
| if (ret == A_OK) { |
| ret = ar6000_htc_raw_open(ar); |
| } |
| } |
| break; |
| |
| case AR6000_XIOCTL_HTC_RAW_CLOSE: |
| if (arRawIfEnabled(ar)) { |
| ret = ar6000_htc_raw_close(ar); |
| arRawIfEnabled(ar) = FALSE; |
| } else { |
| ret = A_ERROR; |
| } |
| break; |
| |
| case AR6000_XIOCTL_HTC_RAW_READ: |
| if (arRawIfEnabled(ar)) { |
| unsigned int streamID; |
| get_user(streamID, (unsigned int *)userdata); |
| get_user(length, (unsigned int *)userdata + 1); |
| buffer = (unsigned char*)rq->ifr_data + sizeof(length); |
| ret = ar6000_htc_raw_read(ar, (HTC_RAW_STREAM_ID)streamID, |
| (char*)buffer, length); |
| put_user(ret, (unsigned int *)rq->ifr_data); |
| } else { |
| ret = A_ERROR; |
| } |
| break; |
| |
| case AR6000_XIOCTL_HTC_RAW_WRITE: |
| if (arRawIfEnabled(ar)) { |
| unsigned int streamID; |
| get_user(streamID, (unsigned int *)userdata); |
| get_user(length, (unsigned int *)userdata + 1); |
| buffer = (unsigned char*)userdata + sizeof(streamID) + sizeof(length); |
| ret = ar6000_htc_raw_write(ar, (HTC_RAW_STREAM_ID)streamID, |
| (char*)buffer, length); |
| put_user(ret, (unsigned int *)rq->ifr_data); |
| } else { |
| ret = A_ERROR; |
| } |
| break; |
| #endif /* HTC_RAW_INTERFACE */ |
| |
| case AR6000_XIOCTL_BMI_LZ_STREAM_START: |
| get_user(address, (unsigned int *)userdata); |
| AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("Start Compressed Stream (address: 0x%x)\n", address)); |
| ret = BMILZStreamStart(hifDevice, address); |
| break; |
| |
| case AR6000_XIOCTL_BMI_LZ_DATA: |
|