| /* |
| * *************************************************************************** |
| * |
| * FILE: sme_native.c |
| * |
| * Copyright (C) 2005-2009 by Cambridge Silicon Radio Ltd. |
| * |
| * Refer to LICENSE.txt included with this source code for details on |
| * the license terms. |
| * |
| * *************************************************************************** |
| */ |
| |
| #include <linux/netdevice.h> |
| #include <linux/version.h> |
| #include "unifi_priv.h" |
| #include "csr_wifi_hip_unifi.h" |
| #include "csr_wifi_hip_conversions.h" |
| |
| static const unsigned char wildcard_address[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| |
| int |
| uf_sme_init(unifi_priv_t *priv) |
| { |
| func_enter(); |
| |
| sema_init(&priv->mlme_blocking_mutex, 1); |
| |
| #ifdef CSR_SUPPORT_WEXT |
| { |
| int r = uf_init_wext_interface(priv); |
| if (r != 0) { |
| func_exit(); |
| return r; |
| } |
| } |
| #endif |
| |
| |
| |
| func_exit(); |
| return 0; |
| } /* uf_sme_init() */ |
| |
| |
| void |
| uf_sme_deinit(unifi_priv_t *priv) |
| { |
| |
| func_enter(); |
| |
| /* Free memory allocated for the scan table */ |
| /* unifi_clear_scan_table(priv); */ |
| |
| /* Cancel any pending workqueue tasks */ |
| flush_workqueue(priv->unifi_workqueue); |
| |
| #ifdef CSR_SUPPORT_WEXT |
| uf_deinit_wext_interface(priv); |
| #endif |
| |
| |
| func_exit(); |
| } /* uf_sme_deinit() */ |
| |
| |
| 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; |
| } |
| |
| int |
| sme_sys_suspend(unifi_priv_t *priv) |
| { |
| const int interfaceNum = 0; /* FIXME */ |
| CsrResult csrResult; |
| |
| /* Abort any pending requests. */ |
| uf_abort_mlme(priv); |
| |
| /* Allow our mlme request to go through. */ |
| priv->io_aborted = 0; |
| |
| /* Send MLME-RESET.req to UniFi. */ |
| unifi_reset_state(priv, priv->netdev[interfaceNum]->dev_addr, 0); |
| |
| /* Stop the network traffic */ |
| netif_carrier_off(priv->netdev[interfaceNum]); |
| |
| /* Put UniFi to deep sleep */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_force_low_power_mode(priv->card); |
| CsrSdioRelease(priv->sdio); |
| |
| return 0; |
| } /* sme_sys_suspend() */ |
| |
| |
| int |
| sme_sys_resume(unifi_priv_t *priv) |
| { |
| #ifdef CSR_SUPPORT_WEXT |
| /* Send disconnect event so clients will re-initialise connection. */ |
| memset(priv->wext_conf.current_ssid, 0, UNIFI_MAX_SSID_LEN); |
| memset((void*)priv->wext_conf.current_bssid, 0, ETH_ALEN); |
| priv->wext_conf.capability = 0; |
| wext_send_disassoc_event(priv); |
| #endif |
| return 0; |
| } /* sme_sys_resume() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * sme_native_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_native_log_event(ul_client_t *pcli, |
| const u8 *sig_packed, int sig_len, |
| const bulk_data_param_t *bulkdata, |
| int dir) |
| { |
| unifi_priv_t *priv; |
| udi_log_t *logptr; |
| u8 *p; |
| int i, r; |
| int signal_len; |
| int total_len; |
| udi_msg_t *msgptr; |
| CSR_SIGNAL signal; |
| ul_client_t *client = pcli; |
| |
| func_enter(); |
| |
| if (client == NULL) { |
| unifi_error(NULL, "sme_native_log_event: client has exited\n"); |
| return; |
| } |
| |
| priv = uf_find_instance(client->instance); |
| if (!priv) { |
| unifi_error(priv, "invalid priv\n"); |
| return; |
| } |
| |
| /* Just a sanity check */ |
| if ((sig_packed == NULL) || (sig_len <= 0)) { |
| return; |
| } |
| |
| /* Get the unpacked signal */ |
| r = read_unpack_signal(sig_packed, &signal); |
| if (r == 0) { |
| signal_len = SigGetSize(&signal); |
| } else { |
| u16 receiver_id = CSR_GET_UINT16_FROM_LITTLE_ENDIAN((sig_packed) + sizeof(u16)) & 0xFF00; |
| |
| /* The control indications are 1 byte, pass them to client. */ |
| if (sig_len == 1) { |
| unifi_trace(priv, UDBG5, |
| "Control indication (0x%x) for native SME.\n", |
| *sig_packed); |
| |
| *(u8*)&signal = *sig_packed; |
| signal_len = sig_len; |
| } else if (receiver_id == 0) { |
| /* |
| * Also "unknown" signals with a ReceiverId of 0 are passed to the client |
| * without unpacking. (This is a code size optimisation to allow signals |
| * that the driver not interested in to be dropped from the unpack code). |
| */ |
| unifi_trace(priv, UDBG5, |
| "Signal 0x%.4X with ReceiverId 0 for native SME.\n", |
| CSR_GET_UINT16_FROM_LITTLE_ENDIAN(sig_packed)); |
| |
| *(u8*)&signal = *sig_packed; |
| signal_len = sig_len; |
| } else { |
| unifi_error(priv, |
| "sme_native_log_event - Received unknown signal 0x%.4X.\n", |
| CSR_GET_UINT16_FROM_LITTLE_ENDIAN(sig_packed)); |
| return; |
| } |
| } |
| |
| unifi_trace(priv, UDBG3, "sme_native_log_event: signal 0x%.4X for %d\n", |
| signal.SignalPrimitiveHeader.SignalId, |
| client->client_id); |
| |
| total_len = signal_len; |
| /* Calculate the buffer we need to store signal plus bulk data */ |
| for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++) { |
| total_len += bulkdata->d[i].data_length; |
| } |
| |
| /* Allocate log structure plus actual signal. */ |
| logptr = (udi_log_t *)kmalloc(sizeof(udi_log_t) + total_len, GFP_KERNEL); |
| |
| if (logptr == NULL) { |
| unifi_error(priv, |
| "Failed to allocate %d bytes for a UDI log record\n", |
| sizeof(udi_log_t) + total_len); |
| return; |
| } |
| |
| /* Fill in udi_log struct */ |
| INIT_LIST_HEAD(&logptr->q); |
| msgptr = &logptr->msg; |
| msgptr->length = sizeof(udi_msg_t) + total_len; |
| msgptr->timestamp = jiffies_to_msecs(jiffies); |
| msgptr->direction = dir; |
| msgptr->signal_length = signal_len; |
| |
| /* Copy signal and bulk data to the log */ |
| p = (u8 *)(msgptr + 1); |
| memcpy(p, &signal, signal_len); |
| p += signal_len; |
| |
| /* Append any bulk data */ |
| for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++) { |
| int len = bulkdata->d[i].data_length; |
| |
| /* |
| * Len here might not be the same as the length in the bulk data slot. |
| * The slot length will always be even, but len could be odd. |
| */ |
| if (len > 0) { |
| if (bulkdata->d[i].os_data_ptr) { |
| memcpy(p, bulkdata->d[i].os_data_ptr, len); |
| } else { |
| memset(p, 0, len); |
| } |
| p += len; |
| } |
| } |
| |
| /* Add to tail of log queue */ |
| down(&client->udi_sem); |
| list_add_tail(&logptr->q, &client->udi_log); |
| up(&client->udi_sem); |
| |
| /* Wake any waiting user process */ |
| wake_up_interruptible(&client->udi_wq); |
| |
| func_exit(); |
| |
| } /* sme_native_log_event() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * 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_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, CsrWifiRouterCtrlTrafficStats *stats) |
| { |
| |
| } /* unifi_ta_indicate_sampling() */ |
| |
| |
| void |
| unifi_ta_indicate_l4stats(void *ospriv, |
| u32 rxTcpThroughput, |
| u32 txTcpThroughput, |
| u32 rxUdpThroughput, |
| u32 txUdpThroughput) |
| { |
| |
| } /* unifi_ta_indicate_l4stats() */ |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * uf_native_process_udi_signal |
| * |
| * Process interesting signals from the UDI interface. |
| * |
| * Arguments: |
| * pcli 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 |
| uf_native_process_udi_signal(ul_client_t *pcli, |
| const u8 *packed_signal, int packed_signal_len, |
| const bulk_data_param_t *bulkdata, int dir) |
| { |
| |
| } /* uf_native_process_udi_signal() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * sme_native_mlme_event_handler |
| * |
| * Callback function to be used as the udi_event_callback when registering |
| * as a client. |
| * This function implements a blocking request-reply interface for WEXT. |
| * To use it, a client specifies this function as the udi_event_callback |
| * to ul_register_client(). The signal dispatcher in |
| * unifi_receive_event() will call this function to deliver a signal. |
| * |
| * Arguments: |
| * pcli Pointer to the client instance. |
| * signal Pointer to the received signal. |
| * signal_len Size of the signal structure in bytes. |
| * bulkdata Pointer to structure containing any associated bulk data. |
| * dir Direction of the signal. Zero means from host, |
| * non-zero means to host. |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| void |
| sme_native_mlme_event_handler(ul_client_t *pcli, |
| const u8 *sig_packed, int sig_len, |
| const bulk_data_param_t *bulkdata, |
| int dir) |
| { |
| CSR_SIGNAL signal; |
| int signal_len; |
| unifi_priv_t *priv = uf_find_instance(pcli->instance); |
| int id, r; |
| |
| func_enter(); |
| |
| /* Just a sanity check */ |
| if ((sig_packed == NULL) || (sig_len <= 0)) { |
| return; |
| } |
| |
| /* Get the unpacked signal */ |
| r = read_unpack_signal(sig_packed, &signal); |
| if (r == 0) { |
| signal_len = SigGetSize(&signal); |
| } else { |
| unifi_error(priv, |
| "sme_native_mlme_event_handler - Received unknown signal 0x%.4X.\n", |
| CSR_GET_UINT16_FROM_LITTLE_ENDIAN(sig_packed)); |
| return; |
| } |
| |
| id = signal.SignalPrimitiveHeader.SignalId; |
| unifi_trace(priv, UDBG4, "wext - Process signal 0x%.4X\n", id); |
| |
| /* |
| * Take the appropriate action for the signal. |
| */ |
| switch (id) { |
| /* |
| * Confirm replies from UniFi. |
| * These all have zero or one CSR_DATAREF member. (FIXME: check this is still true for softmac) |
| */ |
| case CSR_MA_PACKET_CONFIRM_ID: |
| case CSR_MLME_RESET_CONFIRM_ID: |
| case CSR_MLME_GET_CONFIRM_ID: |
| case CSR_MLME_SET_CONFIRM_ID: |
| case CSR_MLME_GET_NEXT_CONFIRM_ID: |
| case CSR_MLME_POWERMGT_CONFIRM_ID: |
| case CSR_MLME_SCAN_CONFIRM_ID: |
| case CSR_MLME_HL_SYNC_CONFIRM_ID: |
| case CSR_MLME_MEASURE_CONFIRM_ID: |
| case CSR_MLME_SETKEYS_CONFIRM_ID: |
| case CSR_MLME_DELETEKEYS_CONFIRM_ID: |
| case CSR_MLME_HL_SYNC_CANCEL_CONFIRM_ID: |
| case CSR_MLME_ADD_PERIODIC_CONFIRM_ID: |
| case CSR_MLME_DEL_PERIODIC_CONFIRM_ID: |
| case CSR_MLME_ADD_AUTONOMOUS_SCAN_CONFIRM_ID: |
| case CSR_MLME_DEL_AUTONOMOUS_SCAN_CONFIRM_ID: |
| case CSR_MLME_SET_PACKET_FILTER_CONFIRM_ID: |
| case CSR_MLME_STOP_MEASURE_CONFIRM_ID: |
| case CSR_MLME_PAUSE_AUTONOMOUS_SCAN_CONFIRM_ID: |
| case CSR_MLME_ADD_TRIGGERED_GET_CONFIRM_ID: |
| case CSR_MLME_DEL_TRIGGERED_GET_CONFIRM_ID: |
| case CSR_MLME_ADD_BLACKOUT_CONFIRM_ID: |
| case CSR_MLME_DEL_BLACKOUT_CONFIRM_ID: |
| case CSR_MLME_ADD_RX_TRIGGER_CONFIRM_ID: |
| case CSR_MLME_DEL_RX_TRIGGER_CONFIRM_ID: |
| case CSR_MLME_CONNECT_STATUS_CONFIRM_ID: |
| case CSR_MLME_MODIFY_BSS_PARAMETER_CONFIRM_ID: |
| case CSR_MLME_ADD_TEMPLATE_CONFIRM_ID: |
| case CSR_MLME_CONFIG_QUEUE_CONFIRM_ID: |
| case CSR_MLME_ADD_TSPEC_CONFIRM_ID: |
| case CSR_MLME_DEL_TSPEC_CONFIRM_ID: |
| case CSR_MLME_START_AGGREGATION_CONFIRM_ID: |
| case CSR_MLME_STOP_AGGREGATION_CONFIRM_ID: |
| case CSR_MLME_SM_START_CONFIRM_ID: |
| case CSR_MLME_LEAVE_CONFIRM_ID: |
| case CSR_MLME_SET_TIM_CONFIRM_ID: |
| case CSR_MLME_GET_KEY_SEQUENCE_CONFIRM_ID: |
| case CSR_MLME_SET_CHANNEL_CONFIRM_ID: |
| case CSR_MLME_ADD_MULTICAST_ADDRESS_CONFIRM_ID: |
| case CSR_DEBUG_GENERIC_CONFIRM_ID: |
| unifi_mlme_copy_reply_and_wakeup_client(pcli, &signal, signal_len, bulkdata); |
| break; |
| |
| case CSR_MLME_CONNECTED_INDICATION_ID: |
| /* We currently ignore the connected-ind for softmac f/w development */ |
| unifi_info(priv, "CSR_MLME_CONNECTED_INDICATION_ID ignored\n"); |
| break; |
| |
| default: |
| break; |
| } |
| |
| func_exit(); |
| } /* sme_native_mlme_event_handler() */ |
| |
| |
| |
| /* |
| * ------------------------------------------------------------------------- |
| * unifi_reset_state |
| * |
| * Ensure that a MAC address has been set. |
| * Send the MLME-RESET signal. |
| * This must be called at least once before starting to do any |
| * network activities (e.g. scan, join etc). |
| * |
| * Arguments: |
| * priv Pointer to device private context struct |
| * macaddr Pointer to chip MAC address. |
| * If this is FF:FF:FF:FF:FF:FF it will be replaced |
| * with the MAC address from the chip. |
| * set_default_mib 1 if the f/w must reset the MIB to the default values |
| * 0 otherwise |
| * |
| * Returns: |
| * 0 on success, an error code otherwise. |
| * ------------------------------------------------------------------------- |
| */ |
| int |
| unifi_reset_state(unifi_priv_t *priv, unsigned char *macaddr, |
| unsigned char set_default_mib) |
| { |
| int r = 0; |
| |
| func_enter(); |
| |
| #ifdef CSR_SUPPORT_WEXT |
| /* The reset clears any 802.11 association. */ |
| priv->wext_conf.flag_associated = 0; |
| #endif |
| |
| func_exit(); |
| return r; |
| } /* unifi_reset_state() */ |
| |