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; }
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() */
void uf_sme_config_wq(struct work_struct *work) { unifi_priv_t *priv = container_of(work, unifi_priv_t, sme_config_task); unifi_AppValue sme_app_value; /* Register to receive indications from the SME */ unifi_mgt_event_mask_set_req(priv->smepriv, NULL, unifi_IndWifiOff | unifi_IndConnectionQuality | unifi_IndMediaStatus | unifi_IndMicFailure); sme_app_value.id = unifi_SmeConfigValue; if (sme_mgt_get_value(priv, &sme_app_value)) { unifi_warning(priv, "uf_sme_config_wq: Get unifi_SMEConfigValue failed.\n"); return; } if (priv->if_index == CSR_INDEX_5G) { sme_app_value.unifi_Value_union.smeConfig.ifIndex = unifi_GHZ_5_0; } else { sme_app_value.unifi_Value_union.smeConfig.ifIndex = unifi_GHZ_2_4; } sme_app_value.unifi_Value_union.smeConfig.trustLevel = (unifi_80211dTrustLevel)tl_80211d; if (sme_mgt_set_value(priv, &sme_app_value)) { unifi_warning(priv, "SME config for 802.11d Trust Level and Radio Band failed.\n"); return; } } /* uf_sme_config_wq() */
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; }
/* * --------------------------------------------------------------------------- * unifi_ta_indicate_protocol * * Report that a packet of a particular type has been seen * * Arguments: * drv_priv The device context pointer passed to ta_init. * protocol The protocol type enum value. * direction Whether the packet was a tx or rx. * src_addr The source MAC address from the data packet. * * Returns: * None. * * Notes: * We defer the actual sending to a background workqueue, * see uf_ta_ind_wq(). * --------------------------------------------------------------------------- */ void unifi_ta_indicate_protocol(void *ospriv, CsrWifiRouterCtrlTrafficPacketType packet_type, CsrWifiRouterCtrlProtocolDirection direction, const CsrWifiMacAddress *src_addr) { unifi_priv_t *priv = (unifi_priv_t*)ospriv; if (priv->ta_ind_work.in_use) { unifi_warning(priv, "unifi_ta_indicate_protocol: workqueue item still in use, not sending\n"); return; } if (CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_RX == direction) { u16 interfaceTag = 0; CsrWifiRouterCtrlTrafficProtocolIndSend(priv->CSR_WIFI_SME_IFACEQUEUE, 0, interfaceTag, packet_type, direction, *src_addr); } else { priv->ta_ind_work.packet_type = packet_type; priv->ta_ind_work.direction = direction; priv->ta_ind_work.src_addr = *src_addr; queue_work(priv->unifi_workqueue, &priv->ta_ind_work.task); } } /* unifi_ta_indicate_protocol() */
/* * --------------------------------------------------------------------------- * unifi_ta_indicate_sampling * * Send the TA sampling information to the SME. * * Arguments: * drv_priv The device context pointer passed to ta_init. * stats The TA sampling data to send. * * Returns: * None. * --------------------------------------------------------------------------- */ void unifi_ta_indicate_sampling(void *ospriv, unifi_TrafficStats *stats) { unifi_priv_t *priv = (unifi_priv_t*)ospriv; if (!priv) { return; } if (priv->ta_sample_ind_work.in_use) { unifi_warning(priv, "unifi_ta_indicate_sampling: workqueue item still in use, not sending\n"); return; } priv->ta_sample_ind_work.stats = *stats; queue_work(priv->unifi_workqueue, &priv->ta_sample_ind_work.task); } /* unifi_ta_indicate_sampling() */
/* * --------------------------------------------------------------------------- * unifi_ta_indicate_protocol * * Report that a packet of a particular type has been seen * * Arguments: * drv_priv The device context pointer passed to ta_init. * protocol The protocol type enum value. * direction Whether the packet was a tx or rx. * src_addr The source MAC address from the data packet. * * Returns: * None. * * Notes: * We defer the actual sending to a background workqueue, * see uf_ta_ind_wq(). * --------------------------------------------------------------------------- */ void unifi_ta_indicate_protocol(void *ospriv, unifi_TrafficPacketType packet_type, unifi_ProtocolDirection direction, const unifi_MACAddress *src_addr) { unifi_priv_t *priv = (unifi_priv_t*)ospriv; if (priv->ta_ind_work.in_use) { unifi_warning(priv, "unifi_ta_indicate_protocol: workqueue item still in use, not sending\n"); return; } priv->ta_ind_work.packet_type = packet_type; priv->ta_ind_work.direction = direction; priv->ta_ind_work.src_addr = *src_addr; queue_work(priv->unifi_workqueue, &priv->ta_ind_work.task); } /* unifi_ta_indicate_protocol() */
/* * --------------------------------------------------------------------------- * 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() */
static CsrResult ConvertSdioToCsrSdioResult(int r) { CsrResult csrResult = CSR_RESULT_FAILURE; switch (r) { case 0: csrResult = CSR_RESULT_SUCCESS; break; case -EIO: case -EILSEQ: csrResult = CSR_SDIO_RESULT_CRC_ERROR; break; /* Timeout errors */ case -ETIMEDOUT: case -EBUSY: csrResult = CSR_SDIO_RESULT_TIMEOUT; break; case -ENODEV: case -ENOMEDIUM: csrResult = CSR_SDIO_RESULT_NO_DEVICE; break; case -EINVAL: csrResult = CSR_SDIO_RESULT_INVALID_VALUE; break; case -ENOMEM: case -ENOSYS: case -ERANGE: case -ENXIO: csrResult = CSR_RESULT_FAILURE; break; default: unifi_warning(NULL, "Unrecognised SDIO error code: %d\n", r); break; } return csrResult; }
int sme_mgt_wifi_on(unifi_priv_t *priv) { int r,i; s32 csrResult; if (priv == NULL) { return -EINVAL; } /* Initialize the interface mode to None */ for (i=0; i<CSR_WIFI_NUM_INTERFACES; i++) { priv->interfacePriv[i]->interfaceMode = 0; } /* Set up interface mode so that get_packet_priority() can * select the right QOS priority when WMM is enabled. */ priv->interfacePriv[0]->interfaceMode = CSR_WIFI_ROUTER_CTRL_MODE_STA; r = uf_request_firmware_files(priv, UNIFI_FW_STA); if (r) { unifi_error(priv, "sme_mgt_wifi_on: Failed to get f/w\n"); return r; } /* * The request to initialise UniFi might come while UniFi is running. * We need to block all I/O activity until the reset completes, otherwise * an SDIO error might occur resulting an indication to the SME which * makes it think that the initialisation has failed. */ priv->bh_thread.block_thread = 1; /* Power on UniFi */ CsrSdioClaim(priv->sdio); csrResult = CsrSdioPowerOn(priv->sdio); CsrSdioRelease(priv->sdio); if(csrResult != CSR_RESULT_SUCCESS && csrResult != CSR_SDIO_RESULT_NOT_RESET) { return -EIO; } if (csrResult == CSR_RESULT_SUCCESS) { /* Initialise UniFi hardware */ r = uf_init_hw(priv); if (r) { return r; } } /* Re-enable the I/O thread */ priv->bh_thread.block_thread = 0; /* Disable deep sleep signalling during the firmware initialisation, to * prevent the wakeup mechanism raising the SDIO clock beyond INIT before * the first MLME-RESET.ind. It gets re-enabled at the CONNECTED.ind, * immediately after the MLME-RESET.ind */ csrResult = unifi_configure_low_power_mode(priv->card, UNIFI_LOW_POWER_DISABLED, UNIFI_PERIODIC_WAKE_HOST_DISABLED); if (csrResult != CSR_RESULT_SUCCESS) { unifi_warning(priv, "sme_mgt_wifi_on: unifi_configure_low_power_mode() returned an error\n"); } /* Start the I/O thread */ CsrSdioClaim(priv->sdio); r = uf_init_bh(priv); if (r) { CsrSdioPowerOff(priv->sdio); CsrSdioRelease(priv->sdio); return r; } CsrSdioRelease(priv->sdio); priv->init_progress = UNIFI_INIT_FW_DOWNLOADED; return 0; }
/* * --------------------------------------------------------------------------- * send_signal * * This function queues a signal for sending to UniFi. It first checks * that there is space on the fh_signal_queue for another entry, then * claims any bulk data slots required and copies data into them. Then * increments the fh_signal_queue write count. * * The fh_signal_queue is later processed by the driver bottom half * (in unifi_bh()). * * This function call unifi_pause_xmit() to pause the flow of data plane * packets when: * - the fh_signal_queue ring buffer is full * - there are less than UNIFI_MAX_DATA_REFERENCES (2) bulk data * slots available. * * Arguments: * card Pointer to card context structure * sigptr Pointer to the signal to write to UniFi. * siglen Number of bytes pointer to by sigptr. * bulkdata Array of pointers to an associated bulk data. * sigq To which from-host queue to add the signal. * * Returns: * CSR_RESULT_SUCCESS on success * CSR_WIFI_HIP_RESULT_NO_SPACE if there were insufficient data slots or * no free signal queue entry * * Notes: * Calls unifi_pause_xmit() when the last slots are used. * --------------------------------------------------------------------------- */ static CsrResult send_signal(card_t *card, const CsrUint8 *sigptr, CsrUint32 siglen, const bulk_data_param_t *bulkdata, q_t *sigq, CsrUint32 priority_q, CsrUint32 run_bh) { CsrUint16 i, data_slot_size; card_signal_t *csptr; CsrInt16 qe; CsrResult r; CsrInt16 debug_print = 0; #ifdef CSR_WIFI_RUN_BH_CHECK CsrUint8 from_host_used_slots = 0; #endif data_slot_size = CardGetDataSlotSize(card); /* Check that the fh_data_queue has a free slot */ if (!CSR_WIFI_HIP_Q_SLOTS_FREE(sigq)) { unifi_trace(card->ospriv, UDBG3, "send_signal: %s full\n", sigq->name); return CSR_WIFI_HIP_RESULT_NO_SPACE; } /* * Now add the signal to the From Host signal queue */ /* Get next slot on queue */ qe = CSR_WIFI_HIP_Q_NEXT_W_SLOT(sigq); csptr = CSR_WIFI_HIP_Q_SLOT_DATA(sigq, qe); /* Make up the card_signal struct */ csptr->signal_length = (CsrUint16)siglen; CsrMemCpy((void *)csptr->sigbuf, (void *)sigptr, siglen); for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; ++i) { if ((bulkdata != NULL) && (bulkdata->d[i].data_length != 0)) { CsrUint32 datalen = bulkdata->d[i].data_length; /* Make sure data will fit in a bulk data slot */ if (bulkdata->d[i].os_data_ptr == NULL) { unifi_error(card->ospriv, "send_signal - NULL bulkdata[%d]\n", i); debug_print++; csptr->bulkdata[i].data_length = 0; } else { if (datalen > data_slot_size) { unifi_error(card->ospriv, "send_signal - Invalid data length %u (@%p), " "truncating\n", datalen, bulkdata->d[i].os_data_ptr); datalen = data_slot_size; debug_print++; } /* Store the bulk data info in the soft queue. */ csptr->bulkdata[i].os_data_ptr = (CsrUint8 *)bulkdata->d[i].os_data_ptr; csptr->bulkdata[i].os_net_buf_ptr = (CsrUint8 *)bulkdata->d[i].os_net_buf_ptr; csptr->bulkdata[i].net_buf_length = bulkdata->d[i].net_buf_length; csptr->bulkdata[i].data_length = datalen; } } else { UNIFI_INIT_BULK_DATA(&csptr->bulkdata[i]); } } if (debug_print) { const CsrUint8 *sig = sigptr; unifi_error(card->ospriv, "Signal(%d): %02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x\n", siglen, sig[0], sig[1], sig[2], sig[3], sig[4], sig[5], sig[6], sig[7], sig[8], sig[9], sig[10], sig[11], sig[12], sig[13], sig[14], sig[15]); unifi_error(card->ospriv, "Bulkdata pointer %p(%d), %p(%d)\n", bulkdata != NULL?bulkdata->d[0].os_data_ptr : NULL, bulkdata != NULL?bulkdata->d[0].data_length : 0, bulkdata != NULL?bulkdata->d[1].os_data_ptr : NULL, bulkdata != NULL?bulkdata->d[1].data_length : 0); } /* Advance the written count to say there is a new entry */ CSR_WIFI_HIP_Q_INC_W(sigq); #ifdef CSR_WIFI_RUN_BH_CHECK FROM_HOST_USED_SLOTS(card, from_host_used_slots); if (from_host_used_slots) { RUN_BH(sigq, card, priority_q, run_bh); } #endif /* * Set the flag to say reason for waking was a host request. * Then ask the OS layer to run the unifi_bh. */ if (run_bh == 1) { card->bh_reason_host = 1; r = unifi_run_bh(card->ospriv); if (r != CSR_RESULT_SUCCESS) { unifi_error(card->ospriv, "failed to run bh.\n"); card->bh_reason_host = 0; /* * The bulk data buffer will be freed by the caller. * We need to invalidate the description of the bulk data in our * soft queue, to prevent the core freeing the bulk data again later. */ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; ++i) { if (csptr->bulkdata[i].data_length != 0) { csptr->bulkdata[i].os_data_ptr = csptr->bulkdata[i].os_net_buf_ptr = NULL; csptr->bulkdata[i].net_buf_length = csptr->bulkdata[i].data_length = 0; } } return r; } } #ifndef CSR_WIFI_RUN_BH_CHECK else { unifi_error(card->ospriv, "run_bh=%d, bh not called.\n", run_bh); } #endif /* * Have we used up all the fh signal list entries? */ if (CSR_WIFI_HIP_Q_SLOTS_FREE(sigq) == 0) { /* We have filled the queue, so stop the upper layer. The command queue * is an exception, as suspending due to that being full could delay * resume/retry until new commands or data are received. */ if (sigq != &card->fh_command_queue) { /* * Must call unifi_pause_xmit() *before* setting the paused flag. * (the unifi_pause_xmit call should not be after setting the flag because of the possibility of being interrupted * by the bh thread between our setting the flag and the call to unifi_pause_xmit() * If bh thread then cleared the flag, we would end up paused, but without the flag set) * Instead, setting it afterwards means that if this thread is interrupted by the bh thread * the pause flag is still guaranteed to end up set * However the potential deadlock now is that if bh thread emptied the queue and cleared the flag before this thread's * call to unifi_pause_xmit(), then bh thread may not run again because it will be waiting for * a packet to appear in the queue but nothing ever will because xmit is paused. * So we will end up with the queue paused, and the flag set to say it is paused, but bh never runs to unpause it. * (Note even this bad situation would not persist long in practice, because something else (eg rx, or tx in different queue) * is likely to wake bh thread quite soon) * But to avoid this deadlock completely, after setting the flag we check that there is something left in the queue. * If there is, we know that bh thread has not emptied the queue yet. * Since bh thread checks to unpause the queue *after* taking packets from the queue, we know that it is still going to make at * least one more check to see whether it needs to unpause the queue. So all is well. * If there are no packets in the queue, then the deadlock described above might happen. To make sure it does not, we * unpause the queue here. A possible side effect is that unifi_restart_xmit() may (rarely) be called for second time * unnecessarily, which is harmless */ #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) unifi_debug_log_to_buf("P"); #endif unifi_pause_xmit(card->ospriv, (unifi_TrafficQueue)priority_q); card_tx_q_pause(card, priority_q); if (CSR_WIFI_HIP_Q_SLOTS_USED(sigq) == 0) { card_tx_q_unpause(card, priority_q); unifi_restart_xmit(card->ospriv, (unifi_TrafficQueue) priority_q); } } else { unifi_warning(card->ospriv, "send_signal: fh_cmd_q full, not pausing (run_bh=%d)\n", run_bh); } } func_exit(); return CSR_RESULT_SUCCESS; } /* send_signal() */