static void ipw_rx_notification()

in wireless/intel/ipw2x00/ipw2200.c [4472:4926]


static void ipw_rx_notification(struct ipw_priv *priv,
				       struct ipw_rx_notification *notif)
{
	u16 size = le16_to_cpu(notif->size);

	IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size);

	switch (notif->subtype) {
	case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
			struct notif_association *assoc = &notif->u.assoc;

			switch (assoc->state) {
			case CMAS_ASSOCIATED:{
					IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
						  IPW_DL_ASSOC,
						  "associated: '%*pE' %pM\n",
						  priv->essid_len, priv->essid,
						  priv->bssid);

					switch (priv->ieee->iw_mode) {
					case IW_MODE_INFRA:
						memcpy(priv->ieee->bssid,
						       priv->bssid, ETH_ALEN);
						break;

					case IW_MODE_ADHOC:
						memcpy(priv->ieee->bssid,
						       priv->bssid, ETH_ALEN);

						/* clear out the station table */
						priv->num_stations = 0;

						IPW_DEBUG_ASSOC
						    ("queueing adhoc check\n");
						schedule_delayed_work(
							&priv->adhoc_check,
							le16_to_cpu(priv->
							assoc_request.
							beacon_interval));
						break;
					}

					priv->status &= ~STATUS_ASSOCIATING;
					priv->status |= STATUS_ASSOCIATED;
					schedule_work(&priv->system_config);

#ifdef CONFIG_IPW2200_QOS
#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
			 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control))
					if ((priv->status & STATUS_AUTH) &&
					    (IPW_GET_PACKET_STYPE(&notif->u.raw)
					     == IEEE80211_STYPE_ASSOC_RESP)) {
						if ((sizeof
						     (struct
						      libipw_assoc_response)
						     <= size)
						    && (size <= 2314)) {
							struct
							libipw_rx_stats
							    stats = {
								.len = size - 1,
							};

							IPW_DEBUG_QOS
							    ("QoS Associate "
							     "size %d\n", size);
							libipw_rx_mgt(priv->
									 ieee,
									 (struct
									  libipw_hdr_4addr
									  *)
									 &notif->u.raw, &stats);
						}
					}
#endif

					schedule_work(&priv->link_up);

					break;
				}

			case CMAS_AUTHENTICATED:{
					if (priv->
					    status & (STATUS_ASSOCIATED |
						      STATUS_AUTH)) {
						struct notif_authenticate *auth
						    = &notif->u.auth;
						IPW_DEBUG(IPW_DL_NOTIF |
							  IPW_DL_STATE |
							  IPW_DL_ASSOC,
							  "deauthenticated: '%*pE' %pM: (0x%04X) - %s\n",
							  priv->essid_len,
							  priv->essid,
							  priv->bssid,
							  le16_to_cpu(auth->status),
							  ipw_get_status_code
							  (le16_to_cpu
							   (auth->status)));

						priv->status &=
						    ~(STATUS_ASSOCIATING |
						      STATUS_AUTH |
						      STATUS_ASSOCIATED);

						schedule_work(&priv->link_down);
						break;
					}

					IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
						  IPW_DL_ASSOC,
						  "authenticated: '%*pE' %pM\n",
						  priv->essid_len, priv->essid,
						  priv->bssid);
					break;
				}

			case CMAS_INIT:{
					if (priv->status & STATUS_AUTH) {
						struct
						    libipw_assoc_response
						*resp;
						resp =
						    (struct
						     libipw_assoc_response
						     *)&notif->u.raw;
						IPW_DEBUG(IPW_DL_NOTIF |
							  IPW_DL_STATE |
							  IPW_DL_ASSOC,
							  "association failed (0x%04X): %s\n",
							  le16_to_cpu(resp->status),
							  ipw_get_status_code
							  (le16_to_cpu
							   (resp->status)));
					}

					IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
						  IPW_DL_ASSOC,
						  "disassociated: '%*pE' %pM\n",
						  priv->essid_len, priv->essid,
						  priv->bssid);

					priv->status &=
					    ~(STATUS_DISASSOCIATING |
					      STATUS_ASSOCIATING |
					      STATUS_ASSOCIATED | STATUS_AUTH);
					if (priv->assoc_network
					    && (priv->assoc_network->
						capability &
						WLAN_CAPABILITY_IBSS))
						ipw_remove_current_network
						    (priv);

					schedule_work(&priv->link_down);

					break;
				}

			case CMAS_RX_ASSOC_RESP:
				break;

			default:
				IPW_ERROR("assoc: unknown (%d)\n",
					  assoc->state);
				break;
			}

			break;
		}

	case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
			struct notif_authenticate *auth = &notif->u.auth;
			switch (auth->state) {
			case CMAS_AUTHENTICATED:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
					  "authenticated: '%*pE' %pM\n",
					  priv->essid_len, priv->essid,
					  priv->bssid);
				priv->status |= STATUS_AUTH;
				break;

			case CMAS_INIT:
				if (priv->status & STATUS_AUTH) {
					IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
						  IPW_DL_ASSOC,
						  "authentication failed (0x%04X): %s\n",
						  le16_to_cpu(auth->status),
						  ipw_get_status_code(le16_to_cpu
								      (auth->
								       status)));
				}
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC,
					  "deauthenticated: '%*pE' %pM\n",
					  priv->essid_len, priv->essid,
					  priv->bssid);

				priv->status &= ~(STATUS_ASSOCIATING |
						  STATUS_AUTH |
						  STATUS_ASSOCIATED);

				schedule_work(&priv->link_down);
				break;

			case CMAS_TX_AUTH_SEQ_1:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_1\n");
				break;
			case CMAS_RX_AUTH_SEQ_2:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_2\n");
				break;
			case CMAS_AUTH_SEQ_1_PASS:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
				break;
			case CMAS_AUTH_SEQ_1_FAIL:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
				break;
			case CMAS_TX_AUTH_SEQ_3:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_3\n");
				break;
			case CMAS_RX_AUTH_SEQ_4:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
				break;
			case CMAS_AUTH_SEQ_2_PASS:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
				break;
			case CMAS_AUTH_SEQ_2_FAIL:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
				break;
			case CMAS_TX_ASSOC:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "TX_ASSOC\n");
				break;
			case CMAS_RX_ASSOC_RESP:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "RX_ASSOC_RESP\n");

				break;
			case CMAS_ASSOCIATED:
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
					  IPW_DL_ASSOC, "ASSOCIATED\n");
				break;
			default:
				IPW_DEBUG_NOTIF("auth: failure - %d\n",
						auth->state);
				break;
			}
			break;
		}

	case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
			struct notif_channel_result *x =
			    &notif->u.channel_result;

			if (size == sizeof(*x)) {
				IPW_DEBUG_SCAN("Scan result for channel %d\n",
					       x->channel_num);
			} else {
				IPW_DEBUG_SCAN("Scan result of wrong size %d "
					       "(should be %zd)\n",
					       size, sizeof(*x));
			}
			break;
		}

	case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
			struct notif_scan_complete *x = &notif->u.scan_complete;
			if (size == sizeof(*x)) {
				IPW_DEBUG_SCAN
				    ("Scan completed: type %d, %d channels, "
				     "%d status\n", x->scan_type,
				     x->num_channels, x->status);
			} else {
				IPW_ERROR("Scan completed of wrong size %d "
					  "(should be %zd)\n",
					  size, sizeof(*x));
			}

			priv->status &=
			    ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);

			wake_up_interruptible(&priv->wait_state);
			cancel_delayed_work(&priv->scan_check);

			if (priv->status & STATUS_EXIT_PENDING)
				break;

			priv->ieee->scans++;

#ifdef CONFIG_IPW2200_MONITOR
			if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
				priv->status |= STATUS_SCAN_FORCED;
				schedule_delayed_work(&priv->request_scan, 0);
				break;
			}
			priv->status &= ~STATUS_SCAN_FORCED;
#endif				/* CONFIG_IPW2200_MONITOR */

			/* Do queued direct scans first */
			if (priv->status & STATUS_DIRECT_SCAN_PENDING)
				schedule_delayed_work(&priv->request_direct_scan, 0);

			if (!(priv->status & (STATUS_ASSOCIATED |
					      STATUS_ASSOCIATING |
					      STATUS_ROAMING |
					      STATUS_DISASSOCIATING)))
				schedule_work(&priv->associate);
			else if (priv->status & STATUS_ROAMING) {
				if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
					/* If a scan completed and we are in roam mode, then
					 * the scan that completed was the one requested as a
					 * result of entering roam... so, schedule the
					 * roam work */
					schedule_work(&priv->roam);
				else
					/* Don't schedule if we aborted the scan */
					priv->status &= ~STATUS_ROAMING;
			} else if (priv->status & STATUS_SCAN_PENDING)
				schedule_delayed_work(&priv->request_scan, 0);
			else if (priv->config & CFG_BACKGROUND_SCAN
				 && priv->status & STATUS_ASSOCIATED)
				schedule_delayed_work(&priv->request_scan,
						      round_jiffies_relative(HZ));

			/* Send an empty event to user space.
			 * We don't send the received data on the event because
			 * it would require us to do complex transcoding, and
			 * we want to minimise the work done in the irq handler
			 * Use a request to extract the data.
			 * Also, we generate this even for any scan, regardless
			 * on how the scan was initiated. User space can just
			 * sync on periodic scan to get fresh data...
			 * Jean II */
			if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
				handle_scan_event(priv);
			break;
		}

	case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
			struct notif_frag_length *x = &notif->u.frag_len;

			if (size == sizeof(*x))
				IPW_ERROR("Frag length: %d\n",
					  le16_to_cpu(x->frag_length));
			else
				IPW_ERROR("Frag length of wrong size %d "
					  "(should be %zd)\n",
					  size, sizeof(*x));
			break;
		}

	case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
			struct notif_link_deterioration *x =
			    &notif->u.link_deterioration;

			if (size == sizeof(*x)) {
				IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
					"link deterioration: type %d, cnt %d\n",
					x->silence_notification_type,
					x->silence_count);
				memcpy(&priv->last_link_deterioration, x,
				       sizeof(*x));
			} else {
				IPW_ERROR("Link Deterioration of wrong size %d "
					  "(should be %zd)\n",
					  size, sizeof(*x));
			}
			break;
		}

	case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
			IPW_ERROR("Dino config\n");
			if (priv->hcmd
			    && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
				IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");

			break;
		}

	case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
			struct notif_beacon_state *x = &notif->u.beacon_state;
			if (size != sizeof(*x)) {
				IPW_ERROR
				    ("Beacon state of wrong size %d (should "
				     "be %zd)\n", size, sizeof(*x));
				break;
			}

			if (le32_to_cpu(x->state) ==
			    HOST_NOTIFICATION_STATUS_BEACON_MISSING)
				ipw_handle_missed_beacon(priv,
							 le32_to_cpu(x->
								     number));

			break;
		}

	case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
			struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
			if (size == sizeof(*x)) {
				IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
					  "0x%02x station %d\n",
					  x->key_state, x->security_type,
					  x->station_index);
				break;
			}

			IPW_ERROR
			    ("TGi Tx Key of wrong size %d (should be %zd)\n",
			     size, sizeof(*x));
			break;
		}

	case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
			struct notif_calibration *x = &notif->u.calibration;

			if (size == sizeof(*x)) {
				memcpy(&priv->calib, x, sizeof(*x));
				IPW_DEBUG_INFO("TODO: Calibration\n");
				break;
			}

			IPW_ERROR
			    ("Calibration of wrong size %d (should be %zd)\n",
			     size, sizeof(*x));
			break;
		}

	case HOST_NOTIFICATION_NOISE_STATS:{
			if (size == sizeof(u32)) {
				priv->exp_avg_noise =
				    exponential_average(priv->exp_avg_noise,
				    (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
				    DEPTH_NOISE);
				break;
			}

			IPW_ERROR
			    ("Noise stat is wrong size %d (should be %zd)\n",
			     size, sizeof(u32));
			break;
		}

	default:
		IPW_DEBUG_NOTIF("Unknown notification: "
				"subtype=%d,flags=0x%2x,size=%d\n",
				notif->subtype, notif->flags, size);
	}
}