Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Host AP driver Info Frame processing (part of hostap.o module) */
  3
  4#include <linux/if_arp.h>
  5#include <linux/sched.h>
  6#include <linux/slab.h>
  7#include <linux/export.h>
  8#include <linux/etherdevice.h>
  9#include "hostap_wlan.h"
 10#include "hostap.h"
 11#include "hostap_ap.h"
 12
 13/* Called only as a tasklet (software IRQ) */
 14static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf,
 15				      int left)
 16{
 17	struct hfa384x_comm_tallies *tallies;
 18
 19	if (left < sizeof(struct hfa384x_comm_tallies)) {
 20		printk(KERN_DEBUG "%s: too short (len=%d) commtallies "
 21		       "info frame\n", local->dev->name, left);
 22		return;
 23	}
 24
 25	tallies = (struct hfa384x_comm_tallies *) buf;
 26#define ADD_COMM_TALLIES(name) \
 27local->comm_tallies.name += le16_to_cpu(tallies->name)
 28	ADD_COMM_TALLIES(tx_unicast_frames);
 29	ADD_COMM_TALLIES(tx_multicast_frames);
 30	ADD_COMM_TALLIES(tx_fragments);
 31	ADD_COMM_TALLIES(tx_unicast_octets);
 32	ADD_COMM_TALLIES(tx_multicast_octets);
 33	ADD_COMM_TALLIES(tx_deferred_transmissions);
 34	ADD_COMM_TALLIES(tx_single_retry_frames);
 35	ADD_COMM_TALLIES(tx_multiple_retry_frames);
 36	ADD_COMM_TALLIES(tx_retry_limit_exceeded);
 37	ADD_COMM_TALLIES(tx_discards);
 38	ADD_COMM_TALLIES(rx_unicast_frames);
 39	ADD_COMM_TALLIES(rx_multicast_frames);
 40	ADD_COMM_TALLIES(rx_fragments);
 41	ADD_COMM_TALLIES(rx_unicast_octets);
 42	ADD_COMM_TALLIES(rx_multicast_octets);
 43	ADD_COMM_TALLIES(rx_fcs_errors);
 44	ADD_COMM_TALLIES(rx_discards_no_buffer);
 45	ADD_COMM_TALLIES(tx_discards_wrong_sa);
 46	ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
 47	ADD_COMM_TALLIES(rx_message_in_msg_fragments);
 48	ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
 49#undef ADD_COMM_TALLIES
 50}
 51
 52
 53/* Called only as a tasklet (software IRQ) */
 54static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf,
 55				      int left)
 56{
 57	struct hfa384x_comm_tallies32 *tallies;
 58
 59	if (left < sizeof(struct hfa384x_comm_tallies32)) {
 60		printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 "
 61		       "info frame\n", local->dev->name, left);
 62		return;
 63	}
 64
 65	tallies = (struct hfa384x_comm_tallies32 *) buf;
 66#define ADD_COMM_TALLIES(name) \
 67local->comm_tallies.name += le32_to_cpu(tallies->name)
 68	ADD_COMM_TALLIES(tx_unicast_frames);
 69	ADD_COMM_TALLIES(tx_multicast_frames);
 70	ADD_COMM_TALLIES(tx_fragments);
 71	ADD_COMM_TALLIES(tx_unicast_octets);
 72	ADD_COMM_TALLIES(tx_multicast_octets);
 73	ADD_COMM_TALLIES(tx_deferred_transmissions);
 74	ADD_COMM_TALLIES(tx_single_retry_frames);
 75	ADD_COMM_TALLIES(tx_multiple_retry_frames);
 76	ADD_COMM_TALLIES(tx_retry_limit_exceeded);
 77	ADD_COMM_TALLIES(tx_discards);
 78	ADD_COMM_TALLIES(rx_unicast_frames);
 79	ADD_COMM_TALLIES(rx_multicast_frames);
 80	ADD_COMM_TALLIES(rx_fragments);
 81	ADD_COMM_TALLIES(rx_unicast_octets);
 82	ADD_COMM_TALLIES(rx_multicast_octets);
 83	ADD_COMM_TALLIES(rx_fcs_errors);
 84	ADD_COMM_TALLIES(rx_discards_no_buffer);
 85	ADD_COMM_TALLIES(tx_discards_wrong_sa);
 86	ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
 87	ADD_COMM_TALLIES(rx_message_in_msg_fragments);
 88	ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
 89#undef ADD_COMM_TALLIES
 90}
 91
 92
 93/* Called only as a tasklet (software IRQ) */
 94static void prism2_info_commtallies(local_info_t *local, unsigned char *buf,
 95				    int left)
 96{
 97	if (local->tallies32)
 98		prism2_info_commtallies32(local, buf, left);
 99	else
100		prism2_info_commtallies16(local, buf, left);
101}
102
103
104#ifndef PRISM2_NO_STATION_MODES
105#ifndef PRISM2_NO_DEBUG
106static const char* hfa384x_linkstatus_str(u16 linkstatus)
107{
108	switch (linkstatus) {
109	case HFA384X_LINKSTATUS_CONNECTED:
110		return "Connected";
111	case HFA384X_LINKSTATUS_DISCONNECTED:
112		return "Disconnected";
113	case HFA384X_LINKSTATUS_AP_CHANGE:
114		return "Access point change";
115	case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE:
116		return "Access point out of range";
117	case HFA384X_LINKSTATUS_AP_IN_RANGE:
118		return "Access point in range";
119	case HFA384X_LINKSTATUS_ASSOC_FAILED:
120		return "Association failed";
121	default:
122		return "Unknown";
123	}
124}
125#endif /* PRISM2_NO_DEBUG */
126
127
128/* Called only as a tasklet (software IRQ) */
129static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf,
130				    int left)
131{
132	u16 val;
133	int non_sta_mode;
134
135	/* Alloc new JoinRequests to occur since LinkStatus for the previous
136	 * has been received */
137	local->last_join_time = 0;
138
139	if (left != 2) {
140		printk(KERN_DEBUG "%s: invalid linkstatus info frame "
141		       "length %d\n", local->dev->name, left);
142		return;
143	}
144
145	non_sta_mode = local->iw_mode == IW_MODE_MASTER ||
146		local->iw_mode == IW_MODE_REPEAT ||
147		local->iw_mode == IW_MODE_MONITOR;
148
149	val = buf[0] | (buf[1] << 8);
150	if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) {
151		PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n",
152		       local->dev->name, val, hfa384x_linkstatus_str(val));
153	}
154
155	if (non_sta_mode) {
156		netif_carrier_on(local->dev);
157		netif_carrier_on(local->ddev);
158		return;
159	}
160
161	/* Get current BSSID later in scheduled task */
162	set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info);
163	local->prev_link_status = val;
164	schedule_work(&local->info_queue);
165}
166
167
168static void prism2_host_roaming(local_info_t *local)
169{
170	struct hfa384x_join_request req;
171	struct net_device *dev = local->dev;
172	struct hfa384x_hostscan_result *selected, *entry;
173	int i;
174	unsigned long flags;
175
176	if (local->last_join_time &&
177	    time_before(jiffies, local->last_join_time + 10 * HZ)) {
178		PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been "
179		       "completed - waiting for it before issuing new one\n",
180		       dev->name);
181		return;
182	}
183
184	/* ScanResults are sorted: first ESS results in decreasing signal
185	 * quality then IBSS results in similar order.
186	 * Trivial roaming policy: just select the first entry.
187	 * This could probably be improved by adding hysteresis to limit
188	 * number of handoffs, etc.
189	 *
190	 * Could do periodic RID_SCANREQUEST or Inquire F101 to get new
191	 * ScanResults */
192	spin_lock_irqsave(&local->lock, flags);
193	if (local->last_scan_results == NULL ||
194	    local->last_scan_results_count == 0) {
195		spin_unlock_irqrestore(&local->lock, flags);
196		PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n",
197		       dev->name);
198		return;
199	}
200
201	selected = &local->last_scan_results[0];
202
203	if (local->preferred_ap[0] || local->preferred_ap[1] ||
204	    local->preferred_ap[2] || local->preferred_ap[3] ||
205	    local->preferred_ap[4] || local->preferred_ap[5]) {
206		/* Try to find preferred AP */
207		PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID %pM\n",
208		       dev->name, local->preferred_ap);
209		for (i = 0; i < local->last_scan_results_count; i++) {
210			entry = &local->last_scan_results[i];
211			if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
212			{
213				PDEBUG(DEBUG_EXTRA, "%s: using preferred AP "
214				       "selection\n", dev->name);
215				selected = entry;
216				break;
217			}
218		}
219	}
220
221	memcpy(req.bssid, selected->bssid, ETH_ALEN);
222	req.channel = selected->chid;
223	spin_unlock_irqrestore(&local->lock, flags);
224
225	PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%pM"
226	       " channel=%d\n",
227	       dev->name, req.bssid, le16_to_cpu(req.channel));
228	if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
229				 sizeof(req))) {
230		printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
231	}
232	local->last_join_time = jiffies;
233}
234
235
236static void hostap_report_scan_complete(local_info_t *local)
237{
238	union iwreq_data wrqu;
239
240	/* Inform user space about new scan results (just empty event,
241	 * SIOCGIWSCAN can be used to fetch data */
242	wrqu.data.length = 0;
243	wrqu.data.flags = 0;
244	wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL);
245
246	/* Allow SIOCGIWSCAN handling to occur since we have received
247	 * scanning result */
248	local->scan_timestamp = 0;
249}
250
251
252/* Called only as a tasklet (software IRQ) */
253static void prism2_info_scanresults(local_info_t *local, unsigned char *buf,
254				    int left)
255{
256	u16 *pos;
257	int new_count, i;
258	unsigned long flags;
259	struct hfa384x_scan_result *res;
260	struct hfa384x_hostscan_result *results, *prev;
261
262	if (left < 4) {
263		printk(KERN_DEBUG "%s: invalid scanresult info frame "
264		       "length %d\n", local->dev->name, left);
265		return;
266	}
267
268	pos = (u16 *) buf;
269	pos++;
270	pos++;
271	left -= 4;
272
273	new_count = left / sizeof(struct hfa384x_scan_result);
274	results = kmalloc_array(new_count,
275				sizeof(struct hfa384x_hostscan_result),
276				GFP_ATOMIC);
277	if (results == NULL)
278		return;
279
280	/* Convert to hostscan result format. */
281	res = (struct hfa384x_scan_result *) pos;
282	for (i = 0; i < new_count; i++) {
283		memcpy(&results[i], &res[i],
284		       sizeof(struct hfa384x_scan_result));
285		results[i].atim = 0;
286	}
287
288	spin_lock_irqsave(&local->lock, flags);
289	local->last_scan_type = PRISM2_SCAN;
290	prev = local->last_scan_results;
291	local->last_scan_results = results;
292	local->last_scan_results_count = new_count;
293	spin_unlock_irqrestore(&local->lock, flags);
294	kfree(prev);
295
296	hostap_report_scan_complete(local);
297
298	/* Perform rest of ScanResults handling later in scheduled task */
299	set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info);
300	schedule_work(&local->info_queue);
301}
302
303
304/* Called only as a tasklet (software IRQ) */
305static void prism2_info_hostscanresults(local_info_t *local,
306					unsigned char *buf, int left)
307{
308	int i, result_size, copy_len, new_count;
309	struct hfa384x_hostscan_result *results, *prev;
310	unsigned long flags;
311	__le16 *pos;
312	u8 *ptr;
313
314	wake_up_interruptible(&local->hostscan_wq);
315
316	if (left < 4) {
317		printk(KERN_DEBUG "%s: invalid hostscanresult info frame "
318		       "length %d\n", local->dev->name, left);
319		return;
320	}
321
322	pos = (__le16 *) buf;
323	copy_len = result_size = le16_to_cpu(*pos);
324	if (result_size == 0) {
325		printk(KERN_DEBUG "%s: invalid result_size (0) in "
326		       "hostscanresults\n", local->dev->name);
327		return;
328	}
329	if (copy_len > sizeof(struct hfa384x_hostscan_result))
330		copy_len = sizeof(struct hfa384x_hostscan_result);
331
332	pos++;
333	pos++;
334	left -= 4;
335	ptr = (u8 *) pos;
336
337	new_count = left / result_size;
338	results = kcalloc(new_count, sizeof(struct hfa384x_hostscan_result),
339			  GFP_ATOMIC);
340	if (results == NULL)
341		return;
342
343	for (i = 0; i < new_count; i++) {
344		memcpy(&results[i], ptr, copy_len);
345		ptr += result_size;
346		left -= result_size;
347	}
348
349	if (left) {
350		printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n",
351		       local->dev->name, left, result_size);
352	}
353
354	spin_lock_irqsave(&local->lock, flags);
355	local->last_scan_type = PRISM2_HOSTSCAN;
356	prev = local->last_scan_results;
357	local->last_scan_results = results;
358	local->last_scan_results_count = new_count;
359	spin_unlock_irqrestore(&local->lock, flags);
360	kfree(prev);
361
362	hostap_report_scan_complete(local);
363}
364#endif /* PRISM2_NO_STATION_MODES */
365
366
367/* Called only as a tasklet (software IRQ) */
368void hostap_info_process(local_info_t *local, struct sk_buff *skb)
369{
370	struct hfa384x_info_frame *info;
371	unsigned char *buf;
372	int left;
373#ifndef PRISM2_NO_DEBUG
374	int i;
375#endif /* PRISM2_NO_DEBUG */
376
377	info = (struct hfa384x_info_frame *) skb->data;
378	buf = skb->data + sizeof(*info);
379	left = skb->len - sizeof(*info);
380
381	switch (le16_to_cpu(info->type)) {
382	case HFA384X_INFO_COMMTALLIES:
383		prism2_info_commtallies(local, buf, left);
384		break;
385
386#ifndef PRISM2_NO_STATION_MODES
387	case HFA384X_INFO_LINKSTATUS:
388		prism2_info_linkstatus(local, buf, left);
389		break;
390
391	case HFA384X_INFO_SCANRESULTS:
392		prism2_info_scanresults(local, buf, left);
393		break;
394
395	case HFA384X_INFO_HOSTSCANRESULTS:
396		prism2_info_hostscanresults(local, buf, left);
397		break;
398#endif /* PRISM2_NO_STATION_MODES */
399
400#ifndef PRISM2_NO_DEBUG
401	default:
402		PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n",
403		       local->dev->name, le16_to_cpu(info->len),
404		       le16_to_cpu(info->type));
405		PDEBUG(DEBUG_EXTRA, "Unknown info frame:");
406		for (i = 0; i < (left < 100 ? left : 100); i++)
407			PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]);
408		PDEBUG2(DEBUG_EXTRA, "\n");
409		break;
410#endif /* PRISM2_NO_DEBUG */
411	}
412}
413
414
415#ifndef PRISM2_NO_STATION_MODES
416static void handle_info_queue_linkstatus(local_info_t *local)
417{
418	int val = local->prev_link_status;
419	int connected;
420	union iwreq_data wrqu;
421
422	connected =
423		val == HFA384X_LINKSTATUS_CONNECTED ||
424		val == HFA384X_LINKSTATUS_AP_CHANGE ||
425		val == HFA384X_LINKSTATUS_AP_IN_RANGE;
426
427	if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID,
428				 local->bssid, ETH_ALEN, 1) < 0) {
429		printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
430		       "LinkStatus event\n", local->dev->name);
431	} else {
432		PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=%pM\n",
433		       local->dev->name,
434		       (unsigned char *) local->bssid);
435		if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
436			hostap_add_sta(local->ap, local->bssid);
437	}
438
439	/* Get BSSID if we have a valid AP address */
440	if (connected) {
441		netif_carrier_on(local->dev);
442		netif_carrier_on(local->ddev);
443		memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN);
444	} else {
445		netif_carrier_off(local->dev);
446		netif_carrier_off(local->ddev);
447		eth_zero_addr(wrqu.ap_addr.sa_data);
448	}
449	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
450
451	/*
452	 * Filter out sequential disconnect events in order not to cause a
453	 * flood of SIOCGIWAP events that have a race condition with EAPOL
454	 * frames and can confuse wpa_supplicant about the current association
455	 * status.
456	 */
457	if (connected || local->prev_linkstatus_connected)
458		wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
459	local->prev_linkstatus_connected = connected;
460}
461
462
463static void handle_info_queue_scanresults(local_info_t *local)
464{
465	if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA)
466		prism2_host_roaming(local);
467
468	if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA &&
469	    !is_zero_ether_addr(local->preferred_ap)) {
470		/*
471		 * Firmware seems to be getting into odd state in host_roaming
472		 * mode 2 when hostscan is used without join command, so try
473		 * to fix this by re-joining the current AP. This does not
474		 * actually trigger a new association if the current AP is
475		 * still in the scan results.
476		 */
477		prism2_host_roaming(local);
478	}
479}
480
481
482/* Called only as scheduled task after receiving info frames (used to avoid
483 * pending too much time in HW IRQ handler). */
484static void handle_info_queue(struct work_struct *work)
485{
486	local_info_t *local = container_of(work, local_info_t, info_queue);
487
488	if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS,
489			       &local->pending_info))
490		handle_info_queue_linkstatus(local);
491
492	if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS,
493			       &local->pending_info))
494		handle_info_queue_scanresults(local);
495}
496#endif /* PRISM2_NO_STATION_MODES */
497
498
499void hostap_info_init(local_info_t *local)
500{
501	skb_queue_head_init(&local->info_list);
502#ifndef PRISM2_NO_STATION_MODES
503	INIT_WORK(&local->info_queue, handle_info_queue);
504#endif /* PRISM2_NO_STATION_MODES */
505}
506
507
508EXPORT_SYMBOL(hostap_info_init);
509EXPORT_SYMBOL(hostap_info_process);