/* * --------------------------------------------------------------------------- * FILE: mlme.c * * PURPOSE: * This file provides functions to send MLME requests to the UniFi. * * Copyright (C) 2007-2008 by Cambridge Silicon Radio Ltd. * * Refer to LICENSE.txt included with this source code for details on * the license terms. * * --------------------------------------------------------------------------- */ #include "csr_wifi_hip_unifi.h" #include "unifi_priv.h" /* * --------------------------------------------------------------------------- * unifi_mlme_wait_for_reply * * Wait for a reply after sending a signal. * * Arguments: * priv Pointer to device private context struct * ul_client Pointer to linux client * sig_reply_id ID of the expected reply (defined in sigs.h). * timeout timeout in ms * * Returns: * 0 on success, -ve POSIX code on error. * * Notes: * This function waits for a specific (sig_reply_id) signal from UniFi. * It also match the sequence number of the received (cfm) signal, with * the latest sequence number of the signal (req) we have sent. * These two number match be equal. * Should only be used for waiting xxx.cfm signals and only after * we have sent the matching xxx.req signal to UniFi. * If no response is received within the expected time (timeout), we assume * that the UniFi is busy and return an error. * If the wait is aborted by a kernel signal arriving, we stop waiting. * If a response from UniFi is not what we expected, we discard it and * wait again. This could be a response from an aborted request. If we * see several bad responses we assume we have lost synchronisation with * UniFi. * --------------------------------------------------------------------------- */ static int unifi_mlme_wait_for_reply(unifi_priv_t *priv, ul_client_t *pcli, int sig_reply_id, int timeout) { int retries = 0; long r; long t = timeout; unsigned int sent_seq_no; /* Convert t in ms to jiffies */ t = msecs_to_jiffies(t); do { /* Wait for the confirm or timeout. */ r = wait_event_interruptible_timeout(pcli->udi_wq, (pcli->wake_up_wq_id) || (priv->io_aborted == 1), t); /* Check for general i/o error */ if (priv->io_aborted) { unifi_error(priv, "MLME operation aborted\n"); return -EIO; } /* * If r=0 the request has timed-out. * If r>0 the request has completed successfully. * If r=-ERESTARTSYS an event (kill signal) has interrupted the wait_event. */ if ((r == 0) && (pcli->wake_up_wq_id == 0)) { unifi_error(priv, "mlme_wait: timed-out waiting for 0x%.4X, after %lu msec.\n", sig_reply_id, jiffies_to_msecs(t)); pcli->wake_up_wq_id = 0; return -ETIMEDOUT; } else if (r == -ERESTARTSYS) { unifi_error(priv, "mlme_wait: waiting for 0x%.4X was aborted.\n", sig_reply_id); pcli->wake_up_wq_id = 0; return -EINTR; } else { /* Get the sequence number of the signal that we previously set. */ if (pcli->seq_no != 0) { sent_seq_no = pcli->seq_no - 1; } else { sent_seq_no = 0x0F; } unifi_trace(priv, UDBG5, "Received 0x%.4X, seq: (r:%d, s:%d)\n", pcli->wake_up_wq_id, pcli->wake_seq_no, sent_seq_no); /* The two sequence ids must match. */ if (pcli->wake_seq_no == sent_seq_no) { /* and the signal ids must match. */ if (sig_reply_id == pcli->wake_up_wq_id) { /* Found the expected signal */ break; } else { /* This should never happen ... */ unifi_error(priv, "mlme_wait: mismatching signal id (0x%.4X - exp 0x%.4X) (seq %d)\n", pcli->wake_up_wq_id, sig_reply_id, pcli->wake_seq_no); pcli->wake_up_wq_id = 0; return -EIO; } } /* Wait for the next signal. */ pcli->wake_up_wq_id = 0; retries ++; if (retries >= 3) { unifi_error(priv, "mlme_wait: confirm wait retries exhausted (0x%.4X - exp 0x%.4X)\n", pcli->wake_up_wq_id, sig_reply_id); pcli->wake_up_wq_id = 0; return -EIO; } } } while (1); pcli->wake_up_wq_id = 0; return 0; } /* unifi_mlme_wait_for_reply() */ /* * --------------------------------------------------------------------------- * unifi_mlme_blocking_request * * Send a MLME request signal to UniFi. * * Arguments: * priv Pointer to device private context struct * pcli Pointer to context of calling process * sig Pointer to the signal to send * data_ptrs Pointer to the bulk data of the signal * timeout The request's timeout. * * Returns: * 0 on success, 802.11 result code on error. * --------------------------------------------------------------------------- */ int unifi_mlme_blocking_request(unifi_priv_t *priv, ul_client_t *pcli, CSR_SIGNAL *sig, bulk_data_param_t *data_ptrs, int timeout) { int r; if (sig->SignalPrimitiveHeader.SignalId == 0) { unifi_error(priv, "unifi_mlme_blocking_request: Invalid Signal Id (0x%x)\n", sig->SignalPrimitiveHeader.SignalId); return -EINVAL; } down(&priv->mlme_blocking_mutex); sig->SignalPrimitiveHeader.ReceiverProcessId = 0; sig->SignalPrimitiveHeader.SenderProcessId = pcli->sender_id | pcli->seq_no; unifi_trace(priv, UDBG2, "Send client=%d, S:0x%04X, sig 0x%.4X\n", pcli->client_id, sig->SignalPrimitiveHeader.SenderProcessId, sig->SignalPrimitiveHeader.SignalId); /* Send the signal to UniFi */ r = ul_send_signal_unpacked(priv, sig, data_ptrs); if (r) { up(&priv->mlme_blocking_mutex); unifi_error(priv, "Error queueing MLME REQUEST signal\n"); return r; } unifi_trace(priv, UDBG5, "Send 0x%.4X, seq = %d\n", sig->SignalPrimitiveHeader.SignalId, pcli->seq_no); /* * Advance the sequence number of the last sent signal, only * if the signal has been successfully set. */ pcli->seq_no++; if (pcli->seq_no > 0x0F) { pcli->seq_no = 0; } r = unifi_mlme_wait_for_reply(priv, pcli, (sig->SignalPrimitiveHeader.SignalId + 1), timeout); up(&priv->mlme_blocking_mutex); if (r) { unifi_error(priv, "Error waiting for MLME CONFIRM signal\n"); return r; } return 0; } /* unifi_mlme_blocking_request() */ /* * --------------------------------------------------------------------------- * unifi_mlme_copy_reply_and_wakeup_client * * Copy the reply signal from UniFi to the client's structure * and wake up the waiting client. * * Arguments: * None. * * Returns: * None. * --------------------------------------------------------------------------- */ void unifi_mlme_copy_reply_and_wakeup_client(ul_client_t *pcli, CSR_SIGNAL *signal, int signal_len, const bulk_data_param_t *bulkdata) { int i; /* Copy the signal to the reply */ memcpy(pcli->reply_signal, signal, signal_len); /* Get the sequence number of the signal that woke us up. */ pcli->wake_seq_no = pcli->reply_signal->SignalPrimitiveHeader.ReceiverProcessId & 0x0F; /* Append any bulk data */ for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++) { if (bulkdata->d[i].data_length > 0) { if (bulkdata->d[i].os_data_ptr) { memcpy(pcli->reply_bulkdata[i]->ptr, bulkdata->d[i].os_data_ptr, bulkdata->d[i].data_length); pcli->reply_bulkdata[i]->length = bulkdata->d[i].data_length; } else { pcli->reply_bulkdata[i]->length = 0; } } } /* Wake the requesting MLME function. */ pcli->wake_up_wq_id = pcli->reply_signal->SignalPrimitiveHeader.SignalId; wake_up_interruptible(&pcli->udi_wq); } /* unifi_mlme_copy_reply_and_wakeup_client() */ /* * --------------------------------------------------------------------------- * uf_abort_mlme * * Abort any MLME operation in progress. * This is used in the error recovery mechanism. * * Arguments: * priv Pointer to driver context. * * Returns: * 0 on success. * --------------------------------------------------------------------------- */ int uf_abort_mlme(unifi_priv_t *priv) { ul_client_t *ul_cli; /* Ensure no MLME functions are waiting on a the mlme_event semaphore. */ priv->io_aborted = 1; ul_cli = priv->netdev_client; if (ul_cli) { wake_up_interruptible(&ul_cli->udi_wq); } ul_cli = priv->wext_client; if (ul_cli) { wake_up_interruptible(&ul_cli->udi_wq); } return 0; } /* uf_abort_mlme() */ /* * --------------------------------------------------------------------------- * * Human-readable decoding of Reason and Result codes. * * --------------------------------------------------------------------------- */ struct mlme_code { const char *name; int id; }; static const struct mlme_code Result_codes[] = { { "Success", 0x0000 }, { "Unspecified Failure", 0x0001 }, /* (Reserved) 0x0002 - 0x0009 */ { "Refused Capabilities Mismatch", 0x000A }, /* (Reserved) 0x000B */ { "Refused External Reason", 0x000C }, /* (Reserved) 0x000D - 0x0010 */ { "Refused AP Out Of Memory", 0x0011 }, { "Refused Basic Rates Mismatch", 0x0012 }, /* (Reserved) 0x0013 - 0x001F */ { "Failure", 0x0020 }, /* (Reserved) 0x0021 - 0x0024 */ { "Refused Reason Unspecified", 0x0025 }, { "Invalid Parameters", 0x0026 }, { "Rejected With Suggested Changes", 0x0027 }, /* (Reserved) 0x0028 - 0x002E */ { "Rejected For Delay Period", 0x002F }, { "Not Allowed", 0x0030 }, { "Not Present", 0x0031 }, { "Not QSTA", 0x0032 }, /* (Reserved) 0x0033 - 0x7FFF */ { "Timeout", 0x8000 }, { "Too Many Simultaneous Requests", 0x8001 }, { "BSS Already Started Or Joined", 0x8002 }, { "Not Supported", 0x8003 }, { "Transmission Failure", 0x8004 }, { "Refused Not Authenticated", 0x8005 }, { "Reset Required Before Start", 0x8006 }, { "LM Info Unavailable", 0x8007 }, { NULL, -1 } }; static const struct mlme_code Reason_codes[] = { /* (Reserved) 0x0000 */ { "Unspecified Reason", 0x0001 }, { "Authentication Not Valid", 0x0002 }, { "Deauthenticated Leave BSS", 0x0003 }, { "Disassociated Inactivity", 0x0004 }, { "AP Overload", 0x0005 }, { "Class2 Frame Error", 0x0006 }, { "Class3 Frame Error", 0x0007 }, { "Disassociated Leave BSS", 0x0008 }, { "Association Not Authenticated", 0x0009 }, { "Disassociated Power Capability", 0x000A }, { "Disassociated Supported Channels", 0x000B }, /* (Reserved) 0x000C */ { "Invalid Information Element", 0x000D }, { "Michael MIC Failure", 0x000E }, { "Fourway Handshake Timeout", 0x000F }, { "Group Key Update Timeout", 0x0010 }, { "Handshake Element Different", 0x0011 }, { "Invalid Group Cipher", 0x0012 }, { "Invalid Pairwise Cipher", 0x0013 }, { "Invalid AKMP", 0x0014 }, { "Unsupported RSN IE Version", 0x0015 }, { "Invalid RSN IE Capabilities", 0x0016 }, { "Dot1X Auth Failed", 0x0017 }, { "Cipher Rejected By Policy", 0x0018 }, /* (Reserved) 0x0019 - 0x001F */ { "QoS Unspecified Reason", 0x0020 }, { "QoS Insufficient Bandwidth", 0x0021 }, { "QoS Excessive Not Ack", 0x0022 }, { "QoS TXOP Limit Exceeded", 0x0023 }, { "QSTA Leaving", 0x0024 }, { "End TS, End DLS, End BA", 0x0025 }, { "Unknown TS, Unknown DLS, Unknown BA", 0x0026 }, { "Timeout", 0x0027 }, /* (Reserved) 0x0028 - 0x002C */ { "STAKey Mismatch", 0x002D }, { NULL, -1 } }; static const char * lookup_something(const struct mlme_code *n, int id) { for (; n->name; n++) { if (n->id == id) { return n->name; } } /* not found */ return NULL; } /* lookup_something() */ const char * lookup_result_code(int result) { static char fallback[16]; const char *str; str = lookup_something(Result_codes, result); if (str == NULL) { snprintf(fallback, 16, "%d", result); str = fallback; } return str; } /* lookup_result_code() */ /* * --------------------------------------------------------------------------- * lookup_reason * * Return a description string for a WiFi MLME ReasonCode. * * Arguments: * reason The ReasonCode to interpret. * * Returns: * Pointer to description string. * --------------------------------------------------------------------------- */ const char * lookup_reason_code(int reason) { static char fallback[16]; const char *str; str = lookup_something(Reason_codes, reason); if (str == NULL) { snprintf(fallback, 16, "%d", reason); str = fallback; } return str; } /* lookup_reason_code() */