/* * *************************************************************************** * FILE: unifi_sme.c * * PURPOSE: SME related functions. * * Copyright (C) 2007-2009 by Cambridge Silicon Radio Ltd. * * Refer to LICENSE.txt included with this source code for details on * the license terms. * * *************************************************************************** */ #include "unifi_priv.h" #include "csr_wifi_hip_unifi.h" #include "csr_wifi_hip_conversions.h" #include <linux/sched/rt.h> int convert_sme_error(CsrResult error) { switch (error) { case CSR_RESULT_SUCCESS: return 0; case CSR_RESULT_FAILURE: case CSR_WIFI_RESULT_NOT_FOUND: case CSR_WIFI_RESULT_TIMED_OUT: case CSR_WIFI_RESULT_CANCELLED: case CSR_WIFI_RESULT_UNAVAILABLE: return -EIO; case CSR_WIFI_RESULT_NO_ROOM: return -EBUSY; case CSR_WIFI_RESULT_INVALID_PARAMETER: return -EINVAL; case CSR_WIFI_RESULT_UNSUPPORTED: return -EOPNOTSUPP; default: return -EIO; } } /* * --------------------------------------------------------------------------- * sme_log_event * * Callback function to be registered as the SME event callback. * Copies the signal content into a new udi_log_t struct and adds * it to the read queue for the SME client. * * Arguments: * arg This is the value given to unifi_add_udi_hook, in * this case a pointer to the client instance. * signal Pointer to the received signal. * signal_len Size of the signal structure in bytes. * bulkdata Pointers to any associated bulk data. * dir Direction of the signal. Zero means from host, * non-zero means to host. * * Returns: * None. * --------------------------------------------------------------------------- */ void sme_log_event(ul_client_t *pcli, const u8 *signal, int signal_len, const bulk_data_param_t *bulkdata, int dir) { unifi_priv_t *priv; CSR_SIGNAL unpacked_signal; CsrWifiSmeDataBlock mlmeCommand; CsrWifiSmeDataBlock dataref1; CsrWifiSmeDataBlock dataref2; CsrResult result = CSR_RESULT_SUCCESS; int r; /* Just a sanity check */ if ((signal == NULL) || (signal_len <= 0)) { return; } priv = uf_find_instance(pcli->instance); if (!priv) { unifi_error(priv, "sme_log_event: invalid priv\n"); return; } if (priv->smepriv == NULL) { unifi_error(priv, "sme_log_event: invalid smepriv\n"); return; } unifi_trace(priv, UDBG3, "sme_log_event: Process signal 0x%.4X\n", CSR_GET_UINT16_FROM_LITTLE_ENDIAN(signal)); /* If the signal is known, then do any filtering required, otherwise it pass it to the SME. */ r = read_unpack_signal(signal, &unpacked_signal); if (r == CSR_RESULT_SUCCESS) { if ((unpacked_signal.SignalPrimitiveHeader.SignalId == CSR_DEBUG_STRING_INDICATION_ID) || (unpacked_signal.SignalPrimitiveHeader.SignalId == CSR_DEBUG_WORD16_INDICATION_ID)) { return; } if (unpacked_signal.SignalPrimitiveHeader.SignalId == CSR_MA_PACKET_INDICATION_ID) { u16 frmCtrl; u8 unicastPdu = TRUE; u8 *macHdrLocation; u8 *raddr = NULL, *taddr = NULL; CsrWifiMacAddress peerMacAddress; /* Check if we need to send CsrWifiRouterCtrlMicFailureInd*/ CSR_MA_PACKET_INDICATION *ind = &unpacked_signal.u.MaPacketIndication; macHdrLocation = (u8 *) bulkdata->d[0].os_data_ptr; /* Fetch the frame control value from mac header */ frmCtrl = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(macHdrLocation); /* Point to the addresses */ raddr = macHdrLocation + MAC_HEADER_ADDR1_OFFSET; taddr = macHdrLocation + MAC_HEADER_ADDR2_OFFSET; memcpy(peerMacAddress.a, taddr, ETH_ALEN); if(ind->ReceptionStatus == CSR_MICHAEL_MIC_ERROR) { if (*raddr & 0x1) unicastPdu = FALSE; CsrWifiRouterCtrlMicFailureIndSend (priv->CSR_WIFI_SME_IFACEQUEUE, 0, (ind->VirtualInterfaceIdentifier & 0xff),peerMacAddress, unicastPdu); return; } else { if(ind->ReceptionStatus == CSR_RX_SUCCESS) { u8 pmBit = (frmCtrl & 0x1000)?0x01:0x00; u16 interfaceTag = (ind->VirtualInterfaceIdentifier & 0xff); CsrWifiRouterCtrlStaInfo_t *srcStaInfo = CsrWifiRouterCtrlGetStationRecordFromPeerMacAddress(priv,taddr,interfaceTag); if((srcStaInfo != NULL) && (uf_check_broadcast_bssid(priv, bulkdata)== FALSE)) { uf_process_pm_bit_for_peer(priv,srcStaInfo,pmBit,interfaceTag); /* Update station last activity flag */ srcStaInfo->activity_flag = TRUE; } } } } if (unpacked_signal.SignalPrimitiveHeader.SignalId == CSR_MA_PACKET_CONFIRM_ID) { CSR_MA_PACKET_CONFIRM *cfm = &unpacked_signal.u.MaPacketConfirm; u16 interfaceTag = (cfm->VirtualInterfaceIdentifier & 0xff); netInterface_priv_t *interfacePriv; CSR_MA_PACKET_REQUEST *req; CsrWifiMacAddress peerMacAddress; if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) { unifi_error(priv, "Bad MA_PACKET_CONFIRM interfaceTag %d\n", interfaceTag); return; } unifi_trace(priv,UDBG1,"MA-PACKET Confirm (%x, %x)\n", cfm->HostTag, cfm->TransmissionStatus); interfacePriv = priv->interfacePriv[interfaceTag]; #ifdef CSR_SUPPORT_SME if(interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_AP || interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_P2PGO) { if(cfm->HostTag == interfacePriv->multicastPduHostTag){ uf_process_ma_pkt_cfm_for_ap(priv ,interfaceTag, cfm); } } #endif req = &interfacePriv->m4_signal.u.MaPacketRequest; if(cfm->HostTag & 0x80000000) { if (cfm->TransmissionStatus != CSR_TX_SUCCESSFUL) { result = CSR_RESULT_FAILURE; } #ifdef CSR_SUPPORT_SME memcpy(peerMacAddress.a, req->Ra.x, ETH_ALEN); /* Check if this is a confirm for EAPOL M4 frame and we need to send transmistted ind*/ if (interfacePriv->m4_sent && (cfm->HostTag == interfacePriv->m4_hostTag)) { unifi_trace(priv, UDBG1, "%s: Sending M4 Transmit CFM\n", __FUNCTION__); CsrWifiRouterCtrlM4TransmittedIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, 0, interfaceTag, peerMacAddress, result); interfacePriv->m4_sent = FALSE; interfacePriv->m4_hostTag = 0xffffffff; } #endif /* If EAPOL was requested via router APIs then send cfm else ignore*/ if((cfm->HostTag & 0x80000000) != CSR_WIFI_EAPOL_M4_HOST_TAG) { CsrWifiRouterMaPacketCfmSend((u16)signal[2], cfm->VirtualInterfaceIdentifier, result, (cfm->HostTag & 0x3fffffff), cfm->Rate); } else { unifi_trace(priv, UDBG1, "%s: M4 received from netdevice\n", __FUNCTION__); } return; } } } mlmeCommand.length = signal_len; mlmeCommand.data = (u8*)signal; dataref1.length = bulkdata->d[0].data_length; if (dataref1.length > 0) { dataref1.data = (u8 *) bulkdata->d[0].os_data_ptr; } else { dataref1.data = NULL; } dataref2.length = bulkdata->d[1].data_length; if (dataref2.length > 0) { dataref2.data = (u8 *) bulkdata->d[1].os_data_ptr; } else { dataref2.data = NULL; } CsrWifiRouterCtrlHipIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, mlmeCommand.length, mlmeCommand.data, dataref1.length, dataref1.data, dataref2.length, dataref2.data); } /* sme_log_event() */ /* * --------------------------------------------------------------------------- * uf_sme_port_state * * Return the state of the controlled port. * * Arguments: * priv Pointer to device private context struct * address Pointer to the destination for tx or sender for rx address * queue Controlled or uncontrolled queue * * Returns: * An unifi_ControlledPortAction value. * --------------------------------------------------------------------------- */ CsrWifiRouterCtrlPortAction uf_sme_port_state(unifi_priv_t *priv, unsigned char *address, int queue, u16 interfaceTag) { int i; unifi_port_config_t *port; netInterface_priv_t *interfacePriv; if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) { unifi_error(priv, "uf_sme_port_state: bad interfaceTag\n"); return CSR_WIFI_ROUTER_CTRL_PORT_ACTION_8021X_PORT_CLOSED_DISCARD; } interfacePriv = priv->interfacePriv[interfaceTag]; if (queue == UF_CONTROLLED_PORT_Q) { port = &interfacePriv->controlled_data_port; } else { port = &interfacePriv->uncontrolled_data_port; } if (!port->entries_in_use) { unifi_trace(priv, UDBG5, "No port configurations, return Discard.\n"); return CSR_WIFI_ROUTER_CTRL_PORT_ACTION_8021X_PORT_CLOSED_DISCARD; } /* If the port configuration is common for all destinations, return it. */ if (port->overide_action == UF_DATA_PORT_OVERIDE) { unifi_trace(priv, UDBG5, "Single port configuration (%d).\n", port->port_cfg[0].port_action); return port->port_cfg[0].port_action; } unifi_trace(priv, UDBG5, "Multiple (%d) port configurations.\n", port->entries_in_use); /* If multiple configurations exist.. */ for (i = 0; i < UNIFI_MAX_CONNECTIONS; i++) { /* .. go through the list and match the destination address. */ if (port->port_cfg[i].in_use && memcmp(address, port->port_cfg[i].mac_address.a, ETH_ALEN) == 0) { /* Return the desired action. */ return port->port_cfg[i].port_action; } } /* Could not find any information, return Open. */ unifi_trace(priv, UDBG5, "port configuration not found, return Open.\n"); return CSR_WIFI_ROUTER_CTRL_PORT_ACTION_8021X_PORT_OPEN; } /* uf_sme_port_state() */ /* * --------------------------------------------------------------------------- * uf_sme_port_config_handle * * Return the port config handle of the controlled/uncontrolled port. * * Arguments: * priv Pointer to device private context struct * address Pointer to the destination for tx or sender for rx address * queue Controlled or uncontrolled queue * * Returns: * An unifi_port_cfg_t* . * --------------------------------------------------------------------------- */ unifi_port_cfg_t* uf_sme_port_config_handle(unifi_priv_t *priv, unsigned char *address, int queue, u16 interfaceTag) { int i; unifi_port_config_t *port; netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag]; if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) { unifi_error(priv, "uf_sme_port_config_handle: bad interfaceTag\n"); return NULL; } if (queue == UF_CONTROLLED_PORT_Q) { port = &interfacePriv->controlled_data_port; } else { port = &interfacePriv->uncontrolled_data_port; } if (!port->entries_in_use) { unifi_trace(priv, UDBG5, "No port configurations, return Discard.\n"); return NULL; } /* If the port configuration is common for all destinations, return it. */ if (port->overide_action == UF_DATA_PORT_OVERIDE) { unifi_trace(priv, UDBG5, "Single port configuration (%d).\n", port->port_cfg[0].port_action); if (address) { unifi_trace(priv, UDBG5, "addr[0] = %x, addr[1] = %x, addr[2] = %x, addr[3] = %x\n", address[0], address[1], address[2], address[3]); } return &port->port_cfg[0]; } unifi_trace(priv, UDBG5, "Multiple port configurations.\n"); /* If multiple configurations exist.. */ for (i = 0; i < UNIFI_MAX_CONNECTIONS; i++) { /* .. go through the list and match the destination address. */ if (port->port_cfg[i].in_use && memcmp(address, port->port_cfg[i].mac_address.a, ETH_ALEN) == 0) { /* Return the desired action. */ return &port->port_cfg[i]; } } /* Could not find any information, return Open. */ unifi_trace(priv, UDBG5, "port configuration not found, returning NULL (debug).\n"); return NULL; } /* uf_sme_port_config_handle */ void uf_multicast_list_wq(struct work_struct *work) { unifi_priv_t *priv = container_of(work, unifi_priv_t, multicast_list_task); int i; u16 interfaceTag = 0; CsrWifiMacAddress* multicast_address_list = NULL; int mc_count; u8 *mc_list; netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag]; if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) { unifi_error(priv, "uf_multicast_list_wq: bad interfaceTag\n"); return; } unifi_trace(priv, UDBG5, "uf_multicast_list_wq: list count = %d\n", interfacePriv->mc_list_count); /* Flush the current list */ CsrWifiRouterCtrlMulticastAddressIndSend(priv->CSR_WIFI_SME_IFACEQUEUE,0, interfaceTag, CSR_WIFI_SME_LIST_ACTION_FLUSH, 0, NULL); mc_count = interfacePriv->mc_list_count; mc_list = interfacePriv->mc_list; /* * Allocate a new list, need to free it later * in unifi_mgt_multicast_address_cfm(). */ multicast_address_list = kmalloc(mc_count * sizeof(CsrWifiMacAddress), GFP_KERNEL); if (multicast_address_list == NULL) { return; } for (i = 0; i < mc_count; i++) { memcpy(multicast_address_list[i].a, mc_list, ETH_ALEN); mc_list += ETH_ALEN; } if (priv->smepriv == NULL) { kfree(multicast_address_list); return; } CsrWifiRouterCtrlMulticastAddressIndSend(priv->CSR_WIFI_SME_IFACEQUEUE,0, interfaceTag, CSR_WIFI_SME_LIST_ACTION_ADD, mc_count, multicast_address_list); /* The SME will take a copy of the addreses*/ kfree(multicast_address_list); } int unifi_cfg_power(unifi_priv_t *priv, unsigned char *arg) { unifi_cfg_power_t cfg_power; int rc; int wol; if (get_user(cfg_power, (unifi_cfg_power_t*)(((unifi_cfg_command_t*)arg) + 1))) { unifi_error(priv, "UNIFI_CFG: Failed to get the argument\n"); return -EFAULT; } switch (cfg_power) { case UNIFI_CFG_POWER_OFF: priv->wol_suspend = (enable_wol == UNIFI_WOL_OFF) ? FALSE : TRUE; rc = sme_sys_suspend(priv); if (rc) { return rc; } break; case UNIFI_CFG_POWER_ON: wol = priv->wol_suspend; rc = sme_sys_resume(priv); if (rc) { return rc; } if (wol) { /* Kick the BH to ensure pending transfers are handled when * a suspend happened with card powered. */ unifi_send_signal(priv->card, NULL, 0, NULL); } break; default: unifi_error(priv, "WIFI POWER: Unknown value.\n"); return -EINVAL; } return 0; } int unifi_cfg_power_save(unifi_priv_t *priv, unsigned char *arg) { unifi_cfg_powersave_t cfg_power_save; CsrWifiSmePowerConfig powerConfig; int rc; if (get_user(cfg_power_save, (unifi_cfg_powersave_t*)(((unifi_cfg_command_t*)arg) + 1))) { unifi_error(priv, "UNIFI_CFG: Failed to get the argument\n"); return -EFAULT; } /* Get the coex info from the SME */ rc = sme_mgt_power_config_get(priv, &powerConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Get unifi_PowerConfigValue failed.\n"); return rc; } switch (cfg_power_save) { case UNIFI_CFG_POWERSAVE_NONE: powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_LOW; break; case UNIFI_CFG_POWERSAVE_FAST: powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_MED; break; case UNIFI_CFG_POWERSAVE_FULL: powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_HIGH; break; case UNIFI_CFG_POWERSAVE_AUTO: powerConfig.powerSaveLevel = CSR_WIFI_SME_POWER_SAVE_LEVEL_AUTO; break; default: unifi_error(priv, "POWERSAVE: Unknown value.\n"); return -EINVAL; } rc = sme_mgt_power_config_set(priv, &powerConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Set unifi_PowerConfigValue failed.\n"); } return rc; } int unifi_cfg_power_supply(unifi_priv_t *priv, unsigned char *arg) { unifi_cfg_powersupply_t cfg_power_supply; CsrWifiSmeHostConfig hostConfig; int rc; if (get_user(cfg_power_supply, (unifi_cfg_powersupply_t*)(((unifi_cfg_command_t*)arg) + 1))) { unifi_error(priv, "UNIFI_CFG: Failed to get the argument\n"); return -EFAULT; } /* Get the coex info from the SME */ rc = sme_mgt_host_config_get(priv, &hostConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Get unifi_HostConfigValue failed.\n"); return rc; } switch (cfg_power_supply) { case UNIFI_CFG_POWERSUPPLY_MAINS: hostConfig.powerMode = CSR_WIFI_SME_HOST_POWER_MODE_ACTIVE; break; case UNIFI_CFG_POWERSUPPLY_BATTERIES: hostConfig.powerMode = CSR_WIFI_SME_HOST_POWER_MODE_POWER_SAVE; break; default: unifi_error(priv, "POWERSUPPLY: Unknown value.\n"); return -EINVAL; } rc = sme_mgt_host_config_set(priv, &hostConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Set unifi_HostConfigValue failed.\n"); } return rc; } int unifi_cfg_packet_filters(unifi_priv_t *priv, unsigned char *arg) { unsigned char *tclas_buffer; unsigned int tclas_buffer_length; tclas_t *dhcp_tclas; int rc; /* Free any TCLASs previously allocated */ if (priv->packet_filters.tclas_ies_length) { kfree(priv->filter_tclas_ies); priv->filter_tclas_ies = NULL; } tclas_buffer = ((unsigned char*)arg) + sizeof(unifi_cfg_command_t) + sizeof(unsigned int); if (copy_from_user(&priv->packet_filters, (void*)tclas_buffer, sizeof(uf_cfg_bcast_packet_filter_t))) { unifi_error(priv, "UNIFI_CFG: Failed to get the filter struct\n"); return -EFAULT; } tclas_buffer_length = priv->packet_filters.tclas_ies_length; /* Allocate TCLASs if necessary */ if (priv->packet_filters.dhcp_filter) { priv->packet_filters.tclas_ies_length += sizeof(tclas_t); } if (priv->packet_filters.tclas_ies_length > 0) { priv->filter_tclas_ies = kmalloc(priv->packet_filters.tclas_ies_length, GFP_KERNEL); if (priv->filter_tclas_ies == NULL) { return -ENOMEM; } if (tclas_buffer_length) { tclas_buffer += sizeof(uf_cfg_bcast_packet_filter_t) - sizeof(unsigned char*); if (copy_from_user(priv->filter_tclas_ies, tclas_buffer, tclas_buffer_length)) { unifi_error(priv, "UNIFI_CFG: Failed to get the TCLAS buffer\n"); return -EFAULT; } } } if(priv->packet_filters.dhcp_filter) { /* Append the DHCP tclas IE */ dhcp_tclas = (tclas_t*)(priv->filter_tclas_ies + tclas_buffer_length); memset(dhcp_tclas, 0, sizeof(tclas_t)); dhcp_tclas->element_id = 14; dhcp_tclas->length = sizeof(tcpip_clsfr_t) + 1; dhcp_tclas->user_priority = 0; dhcp_tclas->tcp_ip_cls_fr.cls_fr_type = 1; dhcp_tclas->tcp_ip_cls_fr.version = 4; ((u8*)(&dhcp_tclas->tcp_ip_cls_fr.source_port))[0] = 0x00; ((u8*)(&dhcp_tclas->tcp_ip_cls_fr.source_port))[1] = 0x44; ((u8*)(&dhcp_tclas->tcp_ip_cls_fr.dest_port))[0] = 0x00; ((u8*)(&dhcp_tclas->tcp_ip_cls_fr.dest_port))[1] = 0x43; dhcp_tclas->tcp_ip_cls_fr.protocol = 0x11; dhcp_tclas->tcp_ip_cls_fr.cls_fr_mask = 0x58; //bits: 3,4,6 } rc = sme_mgt_packet_filter_set(priv); return rc; } int unifi_cfg_wmm_qos_info(unifi_priv_t *priv, unsigned char *arg) { u8 wmm_qos_info; int rc = 0; if (get_user(wmm_qos_info, (u8*)(((unifi_cfg_command_t*)arg) + 1))) { unifi_error(priv, "UNIFI_CFG: Failed to get the argument\n"); return -EFAULT; } /* Store the value in the connection info */ priv->connection_config.wmmQosInfo = wmm_qos_info; return rc; } int unifi_cfg_wmm_addts(unifi_priv_t *priv, unsigned char *arg) { u32 addts_tid; u8 addts_ie_length; u8 *addts_ie; u8 *addts_params; CsrWifiSmeDataBlock tspec; CsrWifiSmeDataBlock tclas; int rc; addts_params = (u8*)(((unifi_cfg_command_t*)arg) + 1); if (get_user(addts_tid, (u32*)addts_params)) { unifi_error(priv, "unifi_cfg_wmm_addts: Failed to get the argument\n"); return -EFAULT; } addts_params += sizeof(u32); if (get_user(addts_ie_length, (u8*)addts_params)) { unifi_error(priv, "unifi_cfg_wmm_addts: Failed to get the argument\n"); return -EFAULT; } unifi_trace(priv, UDBG4, "addts: tid = 0x%x ie_length = %d\n", addts_tid, addts_ie_length); addts_ie = kmalloc(addts_ie_length, GFP_KERNEL); if (addts_ie == NULL) { unifi_error(priv, "unifi_cfg_wmm_addts: Failed to malloc %d bytes for addts_ie buffer\n", addts_ie_length); return -ENOMEM; } addts_params += sizeof(u8); rc = copy_from_user(addts_ie, addts_params, addts_ie_length); if (rc) { unifi_error(priv, "unifi_cfg_wmm_addts: Failed to get the addts buffer\n"); kfree(addts_ie); return -EFAULT; } tspec.data = addts_ie; tspec.length = addts_ie_length; tclas.data = NULL; tclas.length = 0; rc = sme_mgt_tspec(priv, CSR_WIFI_SME_LIST_ACTION_ADD, addts_tid, &tspec, &tclas); kfree(addts_ie); return rc; } int unifi_cfg_wmm_delts(unifi_priv_t *priv, unsigned char *arg) { u32 delts_tid; u8 *delts_params; CsrWifiSmeDataBlock tspec; CsrWifiSmeDataBlock tclas; int rc; delts_params = (u8*)(((unifi_cfg_command_t*)arg) + 1); if (get_user(delts_tid, (u32*)delts_params)) { unifi_error(priv, "unifi_cfg_wmm_delts: Failed to get the argument\n"); return -EFAULT; } unifi_trace(priv, UDBG4, "delts: tid = 0x%x\n", delts_tid); tspec.data = tclas.data = NULL; tspec.length = tclas.length = 0; rc = sme_mgt_tspec(priv, CSR_WIFI_SME_LIST_ACTION_REMOVE, delts_tid, &tspec, &tclas); return rc; } int unifi_cfg_strict_draft_n(unifi_priv_t *priv, unsigned char *arg) { u8 strict_draft_n; u8 *strict_draft_n_params; int rc; CsrWifiSmeStaConfig staConfig; CsrWifiSmeDeviceConfig deviceConfig; strict_draft_n_params = (u8*)(((unifi_cfg_command_t*)arg) + 1); if (get_user(strict_draft_n, (u8*)strict_draft_n_params)) { unifi_error(priv, "unifi_cfg_strict_draft_n: Failed to get the argument\n"); return -EFAULT; } unifi_trace(priv, UDBG4, "strict_draft_n: = %s\n", ((strict_draft_n) ? "yes":"no")); rc = sme_mgt_sme_config_get(priv, &staConfig, &deviceConfig); if (rc) { unifi_warning(priv, "unifi_cfg_strict_draft_n: Get unifi_SMEConfigValue failed.\n"); return -EFAULT; } deviceConfig.enableStrictDraftN = strict_draft_n; rc = sme_mgt_sme_config_set(priv, &staConfig, &deviceConfig); if (rc) { unifi_warning(priv, "unifi_cfg_strict_draft_n: Set unifi_SMEConfigValue failed.\n"); rc = -EFAULT; } return rc; } int unifi_cfg_enable_okc(unifi_priv_t *priv, unsigned char *arg) { u8 enable_okc; u8 *enable_okc_params; int rc; CsrWifiSmeStaConfig staConfig; CsrWifiSmeDeviceConfig deviceConfig; enable_okc_params = (u8*)(((unifi_cfg_command_t*)arg) + 1); if (get_user(enable_okc, (u8*)enable_okc_params)) { unifi_error(priv, "unifi_cfg_enable_okc: Failed to get the argument\n"); return -EFAULT; } unifi_trace(priv, UDBG4, "enable_okc: = %s\n", ((enable_okc) ? "yes":"no")); rc = sme_mgt_sme_config_get(priv, &staConfig, &deviceConfig); if (rc) { unifi_warning(priv, "unifi_cfg_enable_okc: Get unifi_SMEConfigValue failed.\n"); return -EFAULT; } staConfig.enableOpportunisticKeyCaching = enable_okc; rc = sme_mgt_sme_config_set(priv, &staConfig, &deviceConfig); if (rc) { unifi_warning(priv, "unifi_cfg_enable_okc: Set unifi_SMEConfigValue failed.\n"); rc = -EFAULT; } return rc; } int unifi_cfg_get_info(unifi_priv_t *priv, unsigned char *arg) { unifi_cfg_get_t get_cmd; char inst_name[IFNAMSIZ]; int rc; if (get_user(get_cmd, (unifi_cfg_get_t*)(((unifi_cfg_command_t*)arg) + 1))) { unifi_error(priv, "UNIFI_CFG: Failed to get the argument\n"); return -EFAULT; } switch (get_cmd) { case UNIFI_CFG_GET_COEX: { CsrWifiSmeCoexInfo coexInfo; /* Get the coex info from the SME */ rc = sme_mgt_coex_info_get(priv, &coexInfo); if (rc) { unifi_error(priv, "UNIFI_CFG: Get unifi_CoexInfoValue failed.\n"); return rc; } /* Copy the info to the out buffer */ if (copy_to_user((void*)arg, &coexInfo, sizeof(CsrWifiSmeCoexInfo))) { unifi_error(priv, "UNIFI_CFG: Failed to copy the coex info\n"); return -EFAULT; } break; } case UNIFI_CFG_GET_POWER_MODE: { CsrWifiSmePowerConfig powerConfig; rc = sme_mgt_power_config_get(priv, &powerConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Get unifi_PowerConfigValue failed.\n"); return rc; } /* Copy the info to the out buffer */ if (copy_to_user((void*)arg, &powerConfig.powerSaveLevel, sizeof(CsrWifiSmePowerSaveLevel))) { unifi_error(priv, "UNIFI_CFG: Failed to copy the power save info\n"); return -EFAULT; } break; } case UNIFI_CFG_GET_POWER_SUPPLY: { CsrWifiSmeHostConfig hostConfig; rc = sme_mgt_host_config_get(priv, &hostConfig); if (rc) { unifi_error(priv, "UNIFI_CFG: Get unifi_HostConfigValue failed.\n"); return rc; } /* Copy the info to the out buffer */ if (copy_to_user((void*)arg, &hostConfig.powerMode, sizeof(CsrWifiSmeHostPowerMode))) { unifi_error(priv, "UNIFI_CFG: Failed to copy the host power mode\n"); return -EFAULT; } break; } case UNIFI_CFG_GET_VERSIONS: break; case UNIFI_CFG_GET_INSTANCE: { u16 InterfaceId=0; uf_net_get_name(priv->netdev[InterfaceId], &inst_name[0], sizeof(inst_name)); /* Copy the info to the out buffer */ if (copy_to_user((void*)arg, &inst_name[0], sizeof(inst_name))) { unifi_error(priv, "UNIFI_CFG: Failed to copy the instance name\n"); return -EFAULT; } } break; case UNIFI_CFG_GET_AP_CONFIG: { #ifdef CSR_SUPPORT_WEXT_AP uf_cfg_ap_config_t cfg_ap_config; memset(&cfg_ap_config, 0, sizeof(cfg_ap_config)); cfg_ap_config.channel = priv->ap_config.channel; cfg_ap_config.beaconInterval = priv->ap_mac_config.beaconInterval; cfg_ap_config.wmmEnabled = priv->ap_mac_config.wmmEnabled; cfg_ap_config.dtimPeriod = priv->ap_mac_config.dtimPeriod; cfg_ap_config.phySupportedBitmap = priv->ap_mac_config.phySupportedBitmap; if (copy_to_user((void*)arg, &cfg_ap_config, sizeof(uf_cfg_ap_config_t))) { unifi_error(priv, "UNIFI_CFG: Failed to copy the AP configuration\n"); return -EFAULT; } #else return -EPERM; #endif } break; default: unifi_error(priv, "unifi_cfg_get_info: Unknown value.\n"); return -EINVAL; } return 0; } #ifdef CSR_SUPPORT_WEXT_AP int uf_configure_supported_rates(u8 * supportedRates, u8 phySupportedBitmap) { int i=0; u8 b=FALSE, g = FALSE, n = FALSE; b = phySupportedBitmap & CSR_WIFI_SME_AP_PHY_SUPPORT_B; n = phySupportedBitmap & CSR_WIFI_SME_AP_PHY_SUPPORT_N; g = phySupportedBitmap & CSR_WIFI_SME_AP_PHY_SUPPORT_G; if(b || g) { supportedRates[i++]=0x82; supportedRates[i++]=0x84; supportedRates[i++]=0x8b; supportedRates[i++]=0x96; } else if(n) { /* For some strange reasons WiFi stack needs both b and g rates*/ supportedRates[i++]=0x02; supportedRates[i++]=0x04; supportedRates[i++]=0x0b; supportedRates[i++]=0x16; supportedRates[i++]=0x0c; supportedRates[i++]=0x12; supportedRates[i++]=0x18; supportedRates[i++]=0x24; supportedRates[i++]=0x30; supportedRates[i++]=0x48; supportedRates[i++]=0x60; supportedRates[i++]=0x6c; } if(g) { if(!b) { supportedRates[i++]=0x8c; supportedRates[i++]=0x98; supportedRates[i++]=0xb0; } else { supportedRates[i++]=0x0c; supportedRates[i++]=0x18; supportedRates[i++]=0x30; } supportedRates[i++]=0x48; supportedRates[i++]=0x12; supportedRates[i++]=0x24; supportedRates[i++]=0x60; supportedRates[i++]=0x6c; } return i; } int unifi_cfg_set_ap_config(unifi_priv_t * priv,unsigned char* arg) { uf_cfg_ap_config_t cfg_ap_config; char *buffer; buffer = ((unsigned char*)arg) + sizeof(unifi_cfg_command_t) + sizeof(unsigned int); if (copy_from_user(&cfg_ap_config, (void*)buffer, sizeof(uf_cfg_ap_config_t))) { unifi_error(priv, "UNIFI_CFG: Failed to get the ap config struct\n"); return -EFAULT; } priv->ap_config.channel = cfg_ap_config.channel; priv->ap_mac_config.dtimPeriod = cfg_ap_config.dtimPeriod; priv->ap_mac_config.beaconInterval = cfg_ap_config.beaconInterval; priv->group_sec_config.apGroupkeyTimeout = cfg_ap_config.groupkeyTimeout; priv->group_sec_config.apStrictGtkRekey = cfg_ap_config.strictGtkRekeyEnabled; priv->group_sec_config.apGmkTimeout = cfg_ap_config.gmkTimeout; priv->group_sec_config.apResponseTimeout = cfg_ap_config.responseTimeout; priv->group_sec_config.apRetransLimit = cfg_ap_config.retransLimit; priv->ap_mac_config.shortSlotTimeEnabled = cfg_ap_config.shortSlotTimeEnabled; priv->ap_mac_config.ctsProtectionType=cfg_ap_config.ctsProtectionType; priv->ap_mac_config.wmmEnabled = cfg_ap_config.wmmEnabled; priv->ap_mac_config.apHtParams.rxStbc=cfg_ap_config.rxStbc; priv->ap_mac_config.apHtParams.rifsModeAllowed=cfg_ap_config.rifsModeAllowed; priv->ap_mac_config.phySupportedBitmap = cfg_ap_config.phySupportedBitmap; priv->ap_mac_config.maxListenInterval=cfg_ap_config.maxListenInterval; priv->ap_mac_config.supportedRatesCount= uf_configure_supported_rates(priv->ap_mac_config.supportedRates,priv->ap_mac_config.phySupportedBitmap); return 0; } #endif #ifdef CSR_SUPPORT_WEXT void uf_sme_config_wq(struct work_struct *work) { CsrWifiSmeStaConfig staConfig; CsrWifiSmeDeviceConfig deviceConfig; unifi_priv_t *priv = container_of(work, unifi_priv_t, sme_config_task); /* Register to receive indications from the SME */ CsrWifiSmeEventMaskSetReqSend(0, CSR_WIFI_SME_INDICATIONS_WIFIOFF | CSR_WIFI_SME_INDICATIONS_CONNECTIONQUALITY | CSR_WIFI_SME_INDICATIONS_MEDIASTATUS | CSR_WIFI_SME_INDICATIONS_MICFAILURE); if (sme_mgt_sme_config_get(priv, &staConfig, &deviceConfig)) { unifi_warning(priv, "uf_sme_config_wq: Get unifi_SMEConfigValue failed.\n"); return; } if (priv->if_index == CSR_INDEX_5G) { staConfig.ifIndex = CSR_WIFI_SME_RADIO_IF_GHZ_5_0; } else { staConfig.ifIndex = CSR_WIFI_SME_RADIO_IF_GHZ_2_4; } deviceConfig.trustLevel = (CsrWifiSme80211dTrustLevel)tl_80211d; if (sme_mgt_sme_config_set(priv, &staConfig, &deviceConfig)) { unifi_warning(priv, "SME config for 802.11d Trust Level and Radio Band failed.\n"); return; } } /* uf_sme_config_wq() */ #endif /* CSR_SUPPORT_WEXT */ /* * --------------------------------------------------------------------------- * uf_ta_ind_wq * * Deferred work queue function to send Traffic Analysis protocols * indications to the SME. * These are done in a deferred work queue for two reasons: * - the CsrWifiRouterCtrl...Send() functions are not safe for atomic context * - we want to load the main driver data path as lightly as possible * * The TA classifications already come from a workqueue. * * Arguments: * work Pointer to work queue item. * * Returns: * None. * --------------------------------------------------------------------------- */ void uf_ta_ind_wq(struct work_struct *work) { struct ta_ind *ind = container_of(work, struct ta_ind, task); unifi_priv_t *priv = container_of(ind, unifi_priv_t, ta_ind_work); u16 interfaceTag = 0; CsrWifiRouterCtrlTrafficProtocolIndSend(priv->CSR_WIFI_SME_IFACEQUEUE,0, interfaceTag, ind->packet_type, ind->direction, ind->src_addr); ind->in_use = 0; } /* uf_ta_ind_wq() */ /* * --------------------------------------------------------------------------- * uf_ta_sample_ind_wq * * Deferred work queue function to send Traffic Analysis sample * indications to the SME. * These are done in a deferred work queue for two reasons: * - the CsrWifiRouterCtrl...Send() functions are not safe for atomic context * - we want to load the main driver data path as lightly as possible * * The TA classifications already come from a workqueue. * * Arguments: * work Pointer to work queue item. * * Returns: * None. * --------------------------------------------------------------------------- */ void uf_ta_sample_ind_wq(struct work_struct *work) { struct ta_sample_ind *ind = container_of(work, struct ta_sample_ind, task); unifi_priv_t *priv = container_of(ind, unifi_priv_t, ta_sample_ind_work); u16 interfaceTag = 0; unifi_trace(priv, UDBG5, "rxtcp %d txtcp %d rxudp %d txudp %d prio %d\n", priv->rxTcpThroughput, priv->txTcpThroughput, priv->rxUdpThroughput, priv->txUdpThroughput, priv->bh_thread.prio); if(priv->rxTcpThroughput > 1000) { if (bh_priority == -1 && priv->bh_thread.prio != 1) { struct sched_param param; priv->bh_thread.prio = 1; unifi_trace(priv, UDBG1, "%s new thread (RT) priority = %d\n", priv->bh_thread.name, priv->bh_thread.prio); param.sched_priority = priv->bh_thread.prio; sched_setscheduler(priv->bh_thread.thread_task, SCHED_FIFO, ¶m); } } else { if (bh_priority == -1 && priv->bh_thread.prio != DEFAULT_PRIO) { struct sched_param param; param.sched_priority = 0; sched_setscheduler(priv->bh_thread.thread_task, SCHED_NORMAL, ¶m); priv->bh_thread.prio = DEFAULT_PRIO; unifi_trace(priv, UDBG1, "%s new thread priority = %d\n", priv->bh_thread.name, priv->bh_thread.prio); set_user_nice(priv->bh_thread.thread_task, PRIO_TO_NICE(priv->bh_thread.prio)); } } CsrWifiRouterCtrlTrafficSampleIndSend(priv->CSR_WIFI_SME_IFACEQUEUE,0, interfaceTag, ind->stats); ind->in_use = 0; } /* uf_ta_sample_ind_wq() */ /* * --------------------------------------------------------------------------- * uf_send_m4_ready_wq * * Deferred work queue function to send M4 ReadyToSend inds to the SME. * These are done in a deferred work queue for two reasons: * - the CsrWifiRouterCtrl...Send() functions are not safe for atomic context * - we want to load the main driver data path as lightly as possible * * Arguments: * work Pointer to work queue item. * * Returns: * None. * --------------------------------------------------------------------------- */ void uf_send_m4_ready_wq(struct work_struct *work) { netInterface_priv_t *InterfacePriv = container_of(work, netInterface_priv_t, send_m4_ready_task); u16 iface = InterfacePriv->InterfaceTag; unifi_priv_t *priv = InterfacePriv->privPtr; CSR_MA_PACKET_REQUEST *req = &InterfacePriv->m4_signal.u.MaPacketRequest; CsrWifiMacAddress peer; unsigned long flags; /* The peer address was stored in the signal */ spin_lock_irqsave(&priv->m4_lock, flags); memcpy(peer.a, req->Ra.x, sizeof(peer.a)); spin_unlock_irqrestore(&priv->m4_lock, flags); /* Send a signal to SME */ CsrWifiRouterCtrlM4ReadyToSendIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, 0, iface, peer); unifi_trace(priv, UDBG1, "M4ReadyToSendInd sent for peer %pMF\n", peer.a); } /* uf_send_m4_ready_wq() */ #if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_WIFI_SECURITY_WAPI_SW_ENCRYPTION)) /* * --------------------------------------------------------------------------- * uf_send_pkt_to_encrypt * * Deferred work queue function to send the WAPI data pkts to SME when unicast KeyId = 1 * These are done in a deferred work queue for two reasons: * - the CsrWifiRouterCtrl...Send() functions are not safe for atomic context * - we want to load the main driver data path as lightly as possible * * Arguments: * work Pointer to work queue item. * * Returns: * None. * --------------------------------------------------------------------------- */ void uf_send_pkt_to_encrypt(struct work_struct *work) { netInterface_priv_t *interfacePriv = container_of(work, netInterface_priv_t, send_pkt_to_encrypt); u16 interfaceTag = interfacePriv->InterfaceTag; unifi_priv_t *priv = interfacePriv->privPtr; u32 pktBulkDataLength; u8 *pktBulkData; unsigned long flags; if (interfacePriv->interfaceMode == CSR_WIFI_ROUTER_CTRL_MODE_STA) { pktBulkDataLength = interfacePriv->wapi_unicast_bulk_data.data_length; if (pktBulkDataLength > 0) { pktBulkData = kmalloc(pktBulkDataLength, GFP_KERNEL); } else { unifi_error(priv, "uf_send_pkt_to_encrypt() : invalid buffer\n"); return; } spin_lock_irqsave(&priv->wapi_lock, flags); /* Copy over the MA PKT REQ bulk data */ memcpy(pktBulkData, (u8*)interfacePriv->wapi_unicast_bulk_data.os_data_ptr, pktBulkDataLength); /* Free any bulk data buffers allocated for the WAPI Data pkt */ unifi_net_data_free(priv, &interfacePriv->wapi_unicast_bulk_data); interfacePriv->wapi_unicast_bulk_data.net_buf_length = 0; interfacePriv->wapi_unicast_bulk_data.data_length = 0; interfacePriv->wapi_unicast_bulk_data.os_data_ptr = interfacePriv->wapi_unicast_bulk_data.os_net_buf_ptr = NULL; spin_unlock_irqrestore(&priv->wapi_lock, flags); CsrWifiRouterCtrlWapiUnicastTxEncryptIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, 0, interfaceTag, pktBulkDataLength, pktBulkData); unifi_trace(priv, UDBG1, "WapiUnicastTxEncryptInd sent to SME\n"); kfree(pktBulkData); /* Would have been copied over by the SME Handler */ } else { unifi_warning(priv, "uf_send_pkt_to_encrypt() is NOT applicable for interface mode - %d\n",interfacePriv->interfaceMode); } }/* uf_send_pkt_to_encrypt() */ #endif