diff options
author | Marcelo Tosatti <marcelo@kvack.org> | 2007-02-10 14:25:27 (GMT) |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 15:00:54 (GMT) |
commit | 876c9d3aeb989cf1961f2c228d309ba5dcfb1172 (patch) | |
tree | 239e9db92d13abc799c1ffc5304d8ec1503dbc61 /drivers/net/wireless/libertas/cmdresp.c | |
parent | 35c3404efa7407811b706453f83d39b2539dcbd0 (diff) | |
download | linux-fsl-qoriq-876c9d3aeb989cf1961f2c228d309ba5dcfb1172.tar.xz |
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
Add the Marvell Libertas 8388 802.11 USB driver.
Signed-off-by: Marcelo Tosatti <marcelo@kvack.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/cmdresp.c')
-rw-r--r-- | drivers/net/wireless/libertas/cmdresp.c | 1031 |
1 files changed, 1031 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 0000000..cdb012c --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -0,0 +1,1031 @@ +/** + * This file contains the handling of command + * responses as well as events generated by firmware. + */ +#include <linux/delay.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +/** + * @brief This function handles disconnect event. it + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +void libertas_mac_event_disconnected(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + if (adapter->connect_status != libertas_connected) + return; + + lbs_pr_debug(1, "Handles disconnect event.\n"); + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + + msleep_interruptible(1000); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + /* Free Tx and Rx packets */ + kfree_skb(priv->adapter->currenttxskb); + priv->adapter->currenttxskb = NULL; + + /* report disconnect to upper layer */ + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + /* reset SNR/NF/RSSI values */ + memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); + memset(adapter->NF, 0x00, sizeof(adapter->NF)); + memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + adapter->rxpd_rate = 0; + lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", + adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", + adapter->previousssid.ssid, adapter->previousssid.ssidlength); + + /* reset internal flags */ + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + adapter->wpa_ie_len = 0; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + + adapter->connect_status = libertas_disconnected; + + /* + * memorize the previous SSID and BSSID + * it could be used for re-assoc + */ + memcpy(&adapter->previousssid, + &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); + memcpy(adapter->previousbssid, + adapter->curbssparams.bssid, ETH_ALEN); + + /* need to erase the current SSID and BSSID info */ + adapter->pattemptedbssdesc = NULL; + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + if (adapter->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); + libertas_ps_wakeup(priv, 0); + } +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to wlan_private structure + * @para event the event id + * @return n/a + */ +static void handle_mic_failureevent(wlan_private * priv, u32 event) +{ + char buf[50]; + + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { + strcat(buf, "unicast "); + } else { + strcat(buf, "multicast "); + } + + libertas_send_iwevcustom_event(priv, buf); +} + +static int wlan_ret_reg_access(wlan_private * priv, + u16 type, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (type) { + case cmd_ret_mac_reg_access: + { + struct cmd_ds_mac_reg_access *reg; + + reg = + (struct cmd_ds_mac_reg_access *)&resp->params. + macreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *reg; + reg = + (struct cmd_ds_bbp_reg_access *)&resp->params. + bbpreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_rf_reg_access: + { + struct cmd_ds_rf_reg_access *reg; + reg = + (struct cmd_ds_rf_reg_access *)&resp->params. + rfreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + default: + LEAVE(); + return -1; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_get_hw_spec(wlan_private * priv, + struct cmd_ds_command *resp) +{ + u32 i; + struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); + + adapter->fwreleasenumber = hwspec->fwreleasenumber; + + lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", + adapter->fwreleasenumber); + lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hwspec->permanentaddr[0], hwspec->permanentaddr[1], + hwspec->permanentaddr[2], hwspec->permanentaddr[3], + hwspec->permanentaddr[4], hwspec->permanentaddr[5]); + lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", + hwspec->hwifversion, hwspec->version); + + adapter->regioncode = le16_to_cpu(hwspec->regioncode); + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (adapter->regioncode == libertas_region_code_to_index[i]) { + adapter->regiontableindex = (u16) i; + break; + } + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + adapter->regioncode = 0x10; + adapter->regiontableindex = 0; + lbs_pr_info( + "unidentified region code, use the default (USA)\n"); + } + + if (adapter->current_addr[0] == 0xff) { + memmove(adapter->current_addr, hwspec->permanentaddr, + ETH_ALEN); + } + + memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + + if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { + ret = -1; + goto done; + } + + if (libertas_set_universaltable(priv, 0)) { + ret = -1; + goto done; + } + + done: + LEAVE(); + return ret; +} + +static int wlan_ret_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" + " extsleepclk=%x\n", sp->error, sp->offset, + sp->stabletime, sp->calcontrol, sp->externalsleepclk); + adapter->sp.sp_error = le16_to_cpu(sp->error); + adapter->sp.sp_offset = le16_to_cpu(sp->offset); + adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); + adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); + adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); + adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_stat(wlan_private * priv, + struct cmd_ds_command *resp) +{ +/* currently adapter->wlan802_11Stat is unused + + struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; + wlan_adapter *adapter = priv->adapter; + + // TODO Convert it to Big endian befor copy + memcpy(&adapter->wlan802_11Stat, + p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); +*/ + return 0; +} + +static int wlan_ret_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 querytype = le16_to_cpu(smib->querytype); + + ENTER(); + + lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, + querytype); + lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", + le16_to_cpu(smib->bufsize)); + + if (querytype == cmd_act_get) { + switch (oid) { + case fragthresh_i: + priv->adapter->fragthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", + priv->adapter->fragthsd); + break; + case rtsthresh_i: + priv->adapter->rtsthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", + priv->adapter->rtsthsd); + break; + case short_retrylim_i: + priv->adapter->txretrycount = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", + priv->adapter->rtsthsd); + break; + default: + break; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_key_material *pkeymaterial = + &resp->params.keymaterial; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pkeymaterial->action); + + ENTER(); + + /* Copy the returned key to driver private data */ + if (action == cmd_act_get) { + u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; + u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet * pkeyparamset = + (struct MrvlIEtype_keyParamSet *) buf_ptr; + struct WLAN_802_11_KEY * pkey; + u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); + u16 param_set_len = le16_to_cpu(pkeyparamset->length); + u8 * end; + u16 key_len = le16_to_cpu(pkeyparamset->keylen); + + end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) + + sizeof (pkeyparamset->length) + + param_set_len; + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_info & KEY_INFO_WPA_UNICAST) + pkey = &adapter->wpa_unicast_key; + else if (key_info & KEY_INFO_WPA_MCAST) + pkey = &adapter->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + if (key_len > sizeof(pkey->key)) + break; + pkey->type = le16_to_cpu(pkeyparamset->keytypeid); + pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); + pkey->len = le16_to_cpu(pkeyparamset->keylen); + memcpy(pkey->key, pkeyparamset->key, pkey->len); + + buf_ptr = end + 1; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); + + lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pAntenna->action); + + if (action == cmd_act_get_rx) + adapter->rxantennamode = + le16_to_cpu(pAntenna->antennamode); + + if (action == cmd_act_get_tx) + adapter->txantennamode = + le16_to_cpu(pAntenna->antennamode); + + lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", + action, le16_to_cpu(pAntenna->antennamode)); + + return 0; +} + +static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rate_adapt_rateset *rates = + &resp->params.rateset; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (rates->action == cmd_act_get) { + adapter->enablehwauto = rates->enablehwauto; + adapter->ratebitmap = rates->bitmap; + } + + LEAVE(); + + return 0; +} + +static int wlan_ret_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; + wlan_adapter *adapter = priv->adapter; + u8 dot11datarate; + + ENTER(); + + lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", + (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); + + dot11datarate = pdatarate->datarate[0]; + if (pdatarate->action == cmd_act_get_tx_rate) { + memcpy(adapter->libertas_supported_rates, pdatarate->datarate, + sizeof(adapter->libertas_supported_rates)); + } + adapter->datarate = libertas_index_to_data_rate(dot11datarate); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_channel *rfchannel = + &resp->params.rfchannel; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(rfchannel->action); + u16 newchannel = le16_to_cpu(rfchannel->currentchannel); + + ENTER(); + + if (action == cmd_opt_802_11_rf_channel_get + && adapter->curbssparams.channel != newchannel) { + lbs_pr_debug(1, "channel Switch: %d to %d\n", + adapter->curbssparams.channel, newchannel); + + /* Update the channel again */ + adapter->curbssparams.channel = newchannel; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + wlan_adapter *adapter = priv->adapter; + + /* store the non average value */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = + le16_to_cpu(rssirsp->noisefloor); + + adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); + adapter->NF[TYPE_BEACON][TYPE_AVG] = + le16_to_cpu(rssirsp->avgnoisefloor); + + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", + adapter->RSSI[TYPE_BEACON][TYPE_AVG]); + + return 0; +} + +static int wlan_ret_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr *pbuf; + pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; + + lbs_pr_debug(1, "eeprom read len=%x\n", + le16_to_cpu(resp->params.rdeeprom.bytecount)); + if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { + pbuf->NOB = 0; + lbs_pr_debug(1, "eeprom read return length is too big\n"); + return -1; + } + pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); + if (pbuf->NOB > 0) { + + memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + lbs_dbg_hex("adapter", (char *)&pbuf->value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + } + return 0; +} + +static int wlan_ret_get_log(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_get_log *logmessage = + (struct cmd_ds_802_11_get_log *)&resp->params.glog; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* TODO Convert it to Big Endian before copy */ + memcpy(&adapter->logmsg, logmessage, + sizeof(struct cmd_ds_802_11_get_log)); + + LEAVE(); + return 0; +} + +static inline int handle_cmd_response(u16 respcmd, + struct cmd_ds_command *resp, + wlan_private *priv) +{ + int ret = 0; + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + + switch (respcmd) { + case cmd_ret_mac_reg_access: + case cmd_ret_bbp_reg_access: + case cmd_ret_rf_reg_access: + ret = wlan_ret_reg_access(priv, respcmd, resp); + break; + + case cmd_ret_hw_spec_info: + ret = wlan_ret_get_hw_spec(priv, resp); + break; + + case cmd_ret_802_11_scan: + ret = libertas_ret_80211_scan(priv, resp); + break; + + case cmd_ret_802_11_get_log: + ret = wlan_ret_get_log(priv, resp); + break; + + case cmd_ret_802_11_associate: + case cmd_ret_802_11_reassociate: + ret = libertas_ret_80211_associate(priv, resp); + break; + + case cmd_ret_802_11_disassociate: + case cmd_ret_802_11_deauthenticate: + ret = libertas_ret_80211_disassociate(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_start: + case cmd_ret_802_11_ad_hoc_join: + ret = libertas_ret_80211_ad_hoc_start(priv, resp); + break; + + case cmd_ret_802_11_stat: + ret = wlan_ret_802_11_stat(priv, resp); + break; + + case cmd_ret_802_11_snmp_mib: + ret = wlan_ret_802_11_snmp_mib(priv, resp); + break; + + case cmd_ret_802_11_rf_tx_power: + ret = wlan_ret_802_11_rf_tx_power(priv, resp); + break; + + case cmd_ret_802_11_set_afc: + case cmd_ret_802_11_get_afc: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.afc, + sizeof(struct cmd_ds_802_11_afc)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + case cmd_ret_802_11_rf_antenna: + ret = wlan_ret_802_11_rf_antenna(priv, resp); + break; + + case cmd_ret_mac_multicast_adr: + case cmd_ret_mac_control: + case cmd_ret_802_11_set_wep: + case cmd_ret_802_11_reset: + case cmd_ret_802_11_authenticate: + case cmd_ret_802_11_radio_control: + case cmd_ret_802_11_beacon_stop: + case cmd_ret_802_11_enable_rsn: + break; + + case cmd_ret_802_11_data_rate: + ret = wlan_ret_802_11_data_rate(priv, resp); + break; + case cmd_ret_802_11_rate_adapt_rateset: + ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); + break; + case cmd_ret_802_11_rf_channel: + ret = wlan_ret_802_11_rf_channel(priv, resp); + break; + + case cmd_ret_802_11_rssi: + ret = wlan_ret_802_11_rssi(priv, resp); + break; + + case cmd_ret_802_11_mac_address: + ret = wlan_ret_802_11_mac_address(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_stop: + ret = libertas_ret_80211_ad_hoc_stop(priv, resp); + break; + + case cmd_ret_802_11_key_material: + lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); + ret = wlan_ret_802_11_key_material(priv, resp); + break; + + case cmd_ret_802_11_eeprom_access: + ret = wlan_ret_802_11_eeprom_access(priv, resp); + break; + + case cmd_ret_802_11d_domain_info: + ret = libertas_ret_802_11d_domain_info(priv, resp); + break; + + case cmd_ret_802_11_sleep_params: + ret = wlan_ret_802_11_sleep_params(priv, resp); + break; + case cmd_ret_802_11_inactivity_timeout: + spin_lock_irqsave(&adapter->driver_lock, flags); + *((u16 *) adapter->cur_cmd->pdata_buf) = + le16_to_cpu(resp->params.inactivity_timeout.timeout); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + + case cmd_ret_802_11_tpc_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.tpccfg, + sizeof(struct cmd_ds_802_11_tpc_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_led_gpio_ctrl: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.ledgpio, + sizeof(struct cmd_ds_802_11_led_ctrl)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_pwr_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.pwrcfg, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + + case cmd_ret_get_tsf: + spin_lock_irqsave(&adapter->driver_lock, flags); + memcpy(priv->adapter->cur_cmd->pdata_buf, + &resp->params.gettsf.tsfvalue, sizeof(u64)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_bt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.bt.addr1, 2 * ETH_ALEN); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_fwt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.fwt, + sizeof(resp->params.fwt)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_mesh_access: + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.mesh, + sizeof(resp->params.mesh)); + break; + case cmd_rte_802_11_tx_rate_query: + priv->adapter->txrate = resp->params.txrate.txrate; + break; + default: + lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + return ret; +} + +int libertas_process_rx_command(wlan_private * priv) +{ + u16 respcmd; + struct cmd_ds_command *resp; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + ulong flags; + u16 result; + + ENTER(); + + lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); + + /* Now we got response from FW, cancel the command timer */ + del_timer(&adapter->command_timer); + + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!adapter->cur_cmd) { + lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); + ret = -1; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + goto done; + } + resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); + + lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, + priv->wlan_dev.upld_len); + + respcmd = le16_to_cpu(resp->command); + + result = le16_to_cpu(resp->result); + + lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, + result, priv->wlan_dev.upld_len); + + if (!(respcmd & 0x8000)) { + lbs_pr_debug(1, "Invalid response to command!"); + adapter->cur_cmd_retcode = -1; + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + /* Store the response code to cur_cmd_retcode. */ + adapter->cur_cmd_retcode = le16_to_cpu(resp->result); + + if (respcmd == cmd_ret_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psmode; + + psmode = &resp->params.psmode; + lbs_pr_debug(1, + "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, psmode->action); + psmode->action = cpu_to_le16(psmode->action); + + if (result) { + lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", + resp->result); + if (adapter->inframode == wlan802_11ibss) { + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * libertas_execute_next_command(). + */ + if (psmode->action == cmd_subcmd_enter_ps) + adapter->psmode = + wlan802_11powermodecam; + } + } else if (psmode->action == cmd_subcmd_enter_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_AWAKE; + + lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); + if (adapter->connect_status != libertas_connected) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_pr_debug(1, + "Disconnected, Going to invoke libertas_ps_wakeup\n"); + + mutex_unlock(&adapter->lock); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + libertas_ps_wakeup(priv, 0); + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + } + } else if (psmode->action == cmd_subcmd_exit_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_FULL_POWER; + lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); + } else { + lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", + psmode->action); + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = 0; + goto done; + } + + if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { + /* Copy the response back to response buffer */ + memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); + + adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", + resp->command, resp->result); + /* + * Handling errors here + */ + switch (respcmd) { + case cmd_ret_hw_spec_info: + case cmd_ret_802_11_reset: + lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); + break; + + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = handle_cmd_response(respcmd, resp, priv); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + WARN_ON(adapter->nr_cmd_pending > 128); + adapter->cur_cmd = NULL; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + mutex_unlock(&adapter->lock); + LEAVE(); + return ret; +} + +int libertas_process_event(wlan_private * priv) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + u32 eventcause; + + spin_lock_irq(&adapter->driver_lock); + eventcause = adapter->eventcause; + spin_unlock_irq(&adapter->driver_lock); + + ENTER(); + + lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); + + switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_pr_debug(1, "EVENT: Deauthenticated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_pr_debug(1, "EVENT: Disassociated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: + lbs_pr_debug(1, "EVENT: Link lost\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_pr_debug(1, "EVENT: SLEEP\n"); + lbs_pr_debug(1, "_"); + + /* handle unexpected PS SLEEP event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); + break; + } + adapter->psstate = PS_STATE_PRE_SLEEP; + + libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); + + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_pr_debug(1, "EVENT: AWAKE \n"); + lbs_pr_debug(1, "|"); + + /* handle unexpected PS AWAKE event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + adapter->psstate = PS_STATE_AWAKE; + + if (adapter->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * adapter->needtowakeup will be set to FALSE + * in libertas_ps_wakeup() + */ + lbs_pr_debug(1, "Waking up...\n"); + libertas_ps_wakeup(priv, 0); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + break; + case MACREG_INT_CODE_MIB_CHANGED: + case MACREG_INT_CODE_INIT_DONE: + break; + + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); + break; + + case MACREG_INT_CODE_RSSI_LOW: + lbs_pr_alert( "EVENT: RSSI_LOW\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + lbs_pr_alert( "EVENT: SNR_LOW\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + lbs_pr_alert( "EVENT: MAX_FAIL\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + lbs_pr_alert( "EVENT: RSSI_HIGH\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + lbs_pr_alert( "EVENT: SNR_HIGH\n"); + break; + + default: + lbs_pr_alert( "EVENT: unknown event id: %#x\n", + eventcause >> SBI_EVENT_CAUSE_SHIFT); + break; + } + + spin_lock_irq(&adapter->driver_lock); + adapter->eventcause = 0; + spin_unlock_irq(&adapter->driver_lock); + LEAVE(); + return ret; +} |