+
+static void rndis_wlan_auth_indication(struct usbnet *usbdev,
+ struct ndis_80211_status_indication *indication,
+ int len)
+{
+ u8 *buf;
+ const char *type;
+ int flags, buflen;
+ bool pairwise_error, group_error;
+ struct ndis_80211_auth_request *auth_req;
+
+ /* must have at least one array entry */
+ if (len < offsetof(struct ndis_80211_status_indication, u) +
+ sizeof(struct ndis_80211_auth_request)) {
+ devinfo(usbdev, "authentication indication: "
+ "too short message (%i)", len);
+ return;
+ }
+
+ buf = (void *)&indication->u.auth_request[0];
+ buflen = len - offsetof(struct ndis_80211_status_indication, u);
+
+ while (buflen >= sizeof(*auth_req)) {
+ auth_req = (void *)buf;
+ type = "unknown";
+ flags = le32_to_cpu(auth_req->flags);
+ pairwise_error = false;
+ group_error = false;
+
+ if (flags & 0x1)
+ type = "reauth request";
+ if (flags & 0x2)
+ type = "key update request";
+ if (flags & 0x6) {
+ pairwise_error = true;
+ type = "pairwise_error";
+ }
+ if (flags & 0xe) {
+ group_error = true;
+ type = "group_error";
+ }
+
+ devinfo(usbdev, "authentication indication: %s (0x%08x)", type,
+ le32_to_cpu(auth_req->flags));
+
+ if (pairwise_error || group_error) {
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure micfailure;
+
+ memset(&micfailure, 0, sizeof(micfailure));
+ if (pairwise_error)
+ micfailure.flags |= IW_MICFAILURE_PAIRWISE;
+ if (group_error)
+ micfailure.flags |= IW_MICFAILURE_GROUP;
+
+ memcpy(micfailure.src_addr.sa_data, auth_req->bssid,
+ ETH_ALEN);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(micfailure);
+ wireless_send_event(usbdev->net, IWEVMICHAELMICFAILURE,
+ &wrqu, (u8 *)&micfailure);
+ }
+
+ buflen -= le32_to_cpu(auth_req->length);
+ buf += le32_to_cpu(auth_req->length);
+ }
+}
+
+static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
+ struct ndis_80211_status_indication *indication,
+ int len)
+{
+ struct ndis_80211_pmkid_cand_list *cand_list;
+ int list_len, expected_len, i;
+
+ if (len < offsetof(struct ndis_80211_status_indication, u) +
+ sizeof(struct ndis_80211_pmkid_cand_list)) {
+ devinfo(usbdev, "pmkid candidate list indication: "
+ "too short message (%i)", len);
+ return;
+ }
+
+ list_len = le32_to_cpu(indication->u.cand_list.num_candidates) *
+ sizeof(struct ndis_80211_pmkid_candidate);
+ expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len +
+ offsetof(struct ndis_80211_status_indication, u);
+
+ if (len < expected_len) {
+ devinfo(usbdev, "pmkid candidate list indication: "
+ "list larger than buffer (%i < %i)",
+ len, expected_len);
+ return;
+ }
+
+ cand_list = &indication->u.cand_list;
+
+ devinfo(usbdev, "pmkid candidate list indication: "
+ "version %i, candidates %i",
+ le32_to_cpu(cand_list->version),
+ le32_to_cpu(cand_list->num_candidates));
+
+ if (le32_to_cpu(cand_list->version) != 1)
+ return;
+
+ for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) {
+ struct iw_pmkid_cand pcand;
+ union iwreq_data wrqu;
+ struct ndis_80211_pmkid_candidate *cand =
+ &cand_list->candidate_list[i];
+
+ devdbg(usbdev, "cand[%i]: flags: 0x%08x, bssid: %pM",
+ i, le32_to_cpu(cand->flags), cand->bssid);
+
+ memset(&pcand, 0, sizeof(pcand));
+ if (le32_to_cpu(cand->flags) & 0x01)
+ pcand.flags |= IW_PMKID_CAND_PREAUTH;
+ pcand.index = i;
+ memcpy(pcand.bssid.sa_data, cand->bssid, ETH_ALEN);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(pcand);
+ wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu,
+ (u8 *)&pcand);
+ }
+}
+
+static void rndis_wlan_media_specific_indication(struct usbnet *usbdev,
+ struct rndis_indicate *msg, int buflen)
+{
+ struct ndis_80211_status_indication *indication;
+ int len, offset;
+
+ offset = offsetof(struct rndis_indicate, status) +
+ le32_to_cpu(msg->offset);
+ len = le32_to_cpu(msg->length);
+
+ if (len < 8) {
+ devinfo(usbdev, "media specific indication, "
+ "ignore too short message (%i < 8)", len);
+ return;
+ }
+
+ if (offset + len > buflen) {
+ devinfo(usbdev, "media specific indication, "
+ "too large to fit to buffer (%i > %i)",
+ offset + len, buflen);
+ return;
+ }
+
+ indication = (void *)((u8 *)msg + offset);
+
+ switch (le32_to_cpu(indication->status_type)) {
+ case NDIS_80211_STATUSTYPE_RADIOSTATE:
+ devinfo(usbdev, "radio state indication: %i",
+ le32_to_cpu(indication->u.radio_status));
+ return;
+
+ case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE:
+ devinfo(usbdev, "media stream mode indication: %i",
+ le32_to_cpu(indication->u.media_stream_mode));
+ return;
+
+ case NDIS_80211_STATUSTYPE_AUTHENTICATION:
+ rndis_wlan_auth_indication(usbdev, indication, len);
+ return;
+
+ case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST:
+ rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len);
+ return;
+
+ default:
+ devinfo(usbdev, "media specific indication: "
+ "unknown status type 0x%08x",
+ le32_to_cpu(indication->status_type));
+ }
+}
+
+
+static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)