567 lines
17 KiB
C
567 lines
17 KiB
C
/*
|
|
* ***************************************************************************
|
|
*
|
|
* 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 "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)
|
|
{
|
|
sema_init(&priv->mlme_blocking_mutex, 1);
|
|
|
|
#ifdef CSR_SUPPORT_WEXT
|
|
{
|
|
int r = uf_init_wext_interface(priv);
|
|
if (r != 0) {
|
|
return r;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
} /* uf_sme_init() */
|
|
|
|
|
|
void
|
|
uf_sme_deinit(unifi_priv_t *priv)
|
|
{
|
|
|
|
/* 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
|
|
|
|
} /* 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;
|
|
|
|
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);
|
|
|
|
} /* 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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
} /* 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;
|
|
|
|
#ifdef CSR_SUPPORT_WEXT
|
|
/* The reset clears any 802.11 association. */
|
|
priv->wext_conf.flag_associated = 0;
|
|
#endif
|
|
|
|
return r;
|
|
} /* unifi_reset_state() */
|
|
|