Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * This file contains the handling of command
0004  * responses as well as events generated by firmware.
0005  */
0006 
0007 #include <linux/hardirq.h>
0008 #include <linux/slab.h>
0009 #include <linux/delay.h>
0010 #include <linux/sched.h>
0011 #include <asm/unaligned.h>
0012 #include <net/cfg80211.h>
0013 
0014 #include "cfg.h"
0015 #include "cmd.h"
0016 
0017 /**
0018  * lbs_mac_event_disconnected - handles disconnect event. It
0019  * reports disconnect to upper layer, clean tx/rx packets,
0020  * reset link state etc.
0021  *
0022  * @priv:   A pointer to struct lbs_private structure
0023  * @locally_generated: indicates disconnect was requested locally
0024  *      (usually by userspace)
0025  *
0026  * returns: n/a
0027  */
0028 void lbs_mac_event_disconnected(struct lbs_private *priv,
0029                 bool locally_generated)
0030 {
0031     unsigned long flags;
0032 
0033     if (priv->connect_status != LBS_CONNECTED)
0034         return;
0035 
0036     /*
0037      * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
0038      * It causes problem in the Supplicant
0039      */
0040     msleep_interruptible(1000);
0041 
0042     if (priv->wdev->iftype == NL80211_IFTYPE_STATION)
0043         lbs_send_disconnect_notification(priv, locally_generated);
0044 
0045     /* report disconnect to upper layer */
0046     netif_stop_queue(priv->dev);
0047     netif_carrier_off(priv->dev);
0048 
0049     /* Free Tx and Rx packets */
0050     spin_lock_irqsave(&priv->driver_lock, flags);
0051     kfree_skb(priv->currenttxskb);
0052     priv->currenttxskb = NULL;
0053     priv->tx_pending_len = 0;
0054     spin_unlock_irqrestore(&priv->driver_lock, flags);
0055 
0056     priv->connect_status = LBS_DISCONNECTED;
0057 
0058     if (priv->psstate != PS_STATE_FULL_POWER) {
0059         /* make firmware to exit PS mode */
0060         lbs_deb_cmd("disconnected, so exit PS mode\n");
0061         lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
0062     }
0063 }
0064 
0065 int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
0066 {
0067     uint16_t respcmd, curcmd;
0068     struct cmd_header *resp;
0069     int ret = 0;
0070     unsigned long flags;
0071     uint16_t result;
0072 
0073     mutex_lock(&priv->lock);
0074     spin_lock_irqsave(&priv->driver_lock, flags);
0075 
0076     if (!priv->cur_cmd) {
0077         lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
0078         ret = -1;
0079         spin_unlock_irqrestore(&priv->driver_lock, flags);
0080         goto done;
0081     }
0082 
0083     resp = (void *)data;
0084     curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
0085     respcmd = le16_to_cpu(resp->command);
0086     result = le16_to_cpu(resp->result);
0087 
0088     lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
0089              respcmd, le16_to_cpu(resp->seqnum), len);
0090     lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
0091 
0092     if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
0093         netdev_info(priv->dev,
0094                 "Received CMD_RESP with invalid sequence %d (expected %d)\n",
0095                 le16_to_cpu(resp->seqnum),
0096                 le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
0097         spin_unlock_irqrestore(&priv->driver_lock, flags);
0098         ret = -1;
0099         goto done;
0100     }
0101     if (respcmd != CMD_RET(curcmd) &&
0102         respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
0103         netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n",
0104                 respcmd, curcmd);
0105         spin_unlock_irqrestore(&priv->driver_lock, flags);
0106         ret = -1;
0107         goto done;
0108     }
0109 
0110     if (resp->result == cpu_to_le16(0x0004)) {
0111         /* 0x0004 means -EAGAIN. Drop the response, let it time out
0112            and be resubmitted */
0113         netdev_info(priv->dev,
0114                 "Firmware returns DEFER to command %x. Will let it time out...\n",
0115                 le16_to_cpu(resp->command));
0116         spin_unlock_irqrestore(&priv->driver_lock, flags);
0117         ret = -1;
0118         goto done;
0119     }
0120 
0121     /* Now we got response from FW, cancel the command timer */
0122     del_timer(&priv->command_timer);
0123     priv->cmd_timed_out = 0;
0124 
0125     if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
0126         /* struct cmd_ds_802_11_ps_mode also contains
0127          * the header
0128          */
0129         struct cmd_ds_802_11_ps_mode *psmode = (void *)resp;
0130         u16 action = le16_to_cpu(psmode->action);
0131 
0132         lbs_deb_host(
0133                "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
0134                result, action);
0135 
0136         if (result) {
0137             lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
0138                     result);
0139             /*
0140              * We should not re-try enter-ps command in
0141              * ad-hoc mode. It takes place in
0142              * lbs_execute_next_command().
0143              */
0144             if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR &&
0145                 action == PS_MODE_ACTION_ENTER_PS)
0146                 priv->psmode = LBS802_11POWERMODECAM;
0147         } else if (action == PS_MODE_ACTION_ENTER_PS) {
0148             priv->needtowakeup = 0;
0149             priv->psstate = PS_STATE_AWAKE;
0150 
0151             lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
0152             if (priv->connect_status != LBS_CONNECTED) {
0153                 /*
0154                  * When Deauth Event received before Enter_PS command
0155                  * response, We need to wake up the firmware.
0156                  */
0157                 lbs_deb_host(
0158                        "disconnected, invoking lbs_ps_wakeup\n");
0159 
0160                 spin_unlock_irqrestore(&priv->driver_lock, flags);
0161                 mutex_unlock(&priv->lock);
0162                 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS,
0163                         false);
0164                 mutex_lock(&priv->lock);
0165                 spin_lock_irqsave(&priv->driver_lock, flags);
0166             }
0167         } else if (action == PS_MODE_ACTION_EXIT_PS) {
0168             priv->needtowakeup = 0;
0169             priv->psstate = PS_STATE_FULL_POWER;
0170             lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
0171         } else {
0172             lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
0173         }
0174 
0175         __lbs_complete_command(priv, priv->cur_cmd, result);
0176         spin_unlock_irqrestore(&priv->driver_lock, flags);
0177 
0178         ret = 0;
0179         goto done;
0180     }
0181 
0182     /* If the command is not successful, cleanup and return failure */
0183     if ((result != 0 || !(respcmd & 0x8000))) {
0184         lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
0185                result, respcmd);
0186         /*
0187          * Handling errors here
0188          */
0189         switch (respcmd) {
0190         case CMD_RET(CMD_GET_HW_SPEC):
0191         case CMD_RET(CMD_802_11_RESET):
0192             lbs_deb_host("CMD_RESP: reset failed\n");
0193             break;
0194 
0195         }
0196         __lbs_complete_command(priv, priv->cur_cmd, result);
0197         spin_unlock_irqrestore(&priv->driver_lock, flags);
0198 
0199         ret = -1;
0200         goto done;
0201     }
0202 
0203     spin_unlock_irqrestore(&priv->driver_lock, flags);
0204 
0205     if (priv->cur_cmd && priv->cur_cmd->callback) {
0206         ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
0207                 resp);
0208     }
0209 
0210     spin_lock_irqsave(&priv->driver_lock, flags);
0211 
0212     if (priv->cur_cmd) {
0213         /* Clean up and Put current command back to cmdfreeq */
0214         __lbs_complete_command(priv, priv->cur_cmd, result);
0215     }
0216     spin_unlock_irqrestore(&priv->driver_lock, flags);
0217 
0218 done:
0219     mutex_unlock(&priv->lock);
0220     return ret;
0221 }
0222 
0223 void lbs_process_event(struct lbs_private *priv, u32 event)
0224 {
0225     struct cmd_header cmd;
0226 
0227     switch (event) {
0228     case MACREG_INT_CODE_LINK_SENSED:
0229         lbs_deb_cmd("EVENT: link sensed\n");
0230         break;
0231 
0232     case MACREG_INT_CODE_DEAUTHENTICATED:
0233         lbs_deb_cmd("EVENT: deauthenticated\n");
0234         lbs_mac_event_disconnected(priv, false);
0235         break;
0236 
0237     case MACREG_INT_CODE_DISASSOCIATED:
0238         lbs_deb_cmd("EVENT: disassociated\n");
0239         lbs_mac_event_disconnected(priv, false);
0240         break;
0241 
0242     case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
0243         lbs_deb_cmd("EVENT: link lost\n");
0244         lbs_mac_event_disconnected(priv, true);
0245         break;
0246 
0247     case MACREG_INT_CODE_PS_SLEEP:
0248         lbs_deb_cmd("EVENT: ps sleep\n");
0249 
0250         /* handle unexpected PS SLEEP event */
0251         if (priv->psstate == PS_STATE_FULL_POWER) {
0252             lbs_deb_cmd(
0253                    "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n");
0254             break;
0255         }
0256         if (!list_empty(&priv->cmdpendingq)) {
0257             lbs_deb_cmd("EVENT: commands in queue, do not sleep\n");
0258             break;
0259         }
0260         priv->psstate = PS_STATE_PRE_SLEEP;
0261 
0262         lbs_ps_confirm_sleep(priv);
0263 
0264         break;
0265 
0266     case MACREG_INT_CODE_HOST_AWAKE:
0267         lbs_deb_cmd("EVENT: host awake\n");
0268         if (priv->reset_deep_sleep_wakeup)
0269             priv->reset_deep_sleep_wakeup(priv);
0270         priv->is_deep_sleep = 0;
0271         lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd,
0272                 sizeof(cmd));
0273         priv->is_host_sleep_activated = 0;
0274         wake_up_interruptible(&priv->host_sleep_q);
0275         break;
0276 
0277     case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
0278         if (priv->reset_deep_sleep_wakeup)
0279             priv->reset_deep_sleep_wakeup(priv);
0280         lbs_deb_cmd("EVENT: ds awake\n");
0281         priv->is_deep_sleep = 0;
0282         priv->wakeup_dev_required = 0;
0283         wake_up_interruptible(&priv->ds_awake_q);
0284         break;
0285 
0286     case MACREG_INT_CODE_PS_AWAKE:
0287         lbs_deb_cmd("EVENT: ps awake\n");
0288         /* handle unexpected PS AWAKE event */
0289         if (priv->psstate == PS_STATE_FULL_POWER) {
0290             lbs_deb_cmd(
0291                    "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
0292             break;
0293         }
0294 
0295         priv->psstate = PS_STATE_AWAKE;
0296 
0297         if (priv->needtowakeup) {
0298             /*
0299              * wait for the command processing to finish
0300              * before resuming sending
0301              * priv->needtowakeup will be set to FALSE
0302              * in lbs_ps_wakeup()
0303              */
0304             lbs_deb_cmd("waking up ...\n");
0305             lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
0306         }
0307         break;
0308 
0309     case MACREG_INT_CODE_MIC_ERR_UNICAST:
0310         lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
0311         lbs_send_mic_failureevent(priv, event);
0312         break;
0313 
0314     case MACREG_INT_CODE_MIC_ERR_MULTICAST:
0315         lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
0316         lbs_send_mic_failureevent(priv, event);
0317         break;
0318 
0319     case MACREG_INT_CODE_MIB_CHANGED:
0320         lbs_deb_cmd("EVENT: MIB CHANGED\n");
0321         break;
0322     case MACREG_INT_CODE_INIT_DONE:
0323         lbs_deb_cmd("EVENT: INIT DONE\n");
0324         break;
0325     case MACREG_INT_CODE_ADHOC_BCN_LOST:
0326         lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
0327         break;
0328     case MACREG_INT_CODE_RSSI_LOW:
0329         netdev_alert(priv->dev, "EVENT: rssi low\n");
0330         break;
0331     case MACREG_INT_CODE_SNR_LOW:
0332         netdev_alert(priv->dev, "EVENT: snr low\n");
0333         break;
0334     case MACREG_INT_CODE_MAX_FAIL:
0335         netdev_alert(priv->dev, "EVENT: max fail\n");
0336         break;
0337     case MACREG_INT_CODE_RSSI_HIGH:
0338         netdev_alert(priv->dev, "EVENT: rssi high\n");
0339         break;
0340     case MACREG_INT_CODE_SNR_HIGH:
0341         netdev_alert(priv->dev, "EVENT: snr high\n");
0342         break;
0343 
0344     case MACREG_INT_CODE_MESH_AUTO_STARTED:
0345         /* Ignore spurious autostart events */
0346         netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n");
0347         break;
0348 
0349     default:
0350         netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event);
0351         break;
0352     }
0353 }