⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sm_drv_ioctl_umac.c

📁 cx3110 drivers for linux 2.6 (基于SPI)
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * src/sm_drv_ioctl_umac.c * * Wireless extensions for Conexant CX3110x chipset. * Based on prism54 ioctls implementation. * * Copyright (C) 2005, 2006 Nokia Corporation * Author: Samuel Ortiz <samuel.ortiz@nokia.com> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/types.h>#include <linux/if_arp.h>#include "sm_drv.h"#include "sm_drv_wpa.h"#include "sm_drv_ioctl.h"extern unsigned int driver_type;/* Wireless events helpers */#define WPA_EID_GENERIC 0xdd#define WPA2_EID_GENERIC 0x30static u8 wpa_oid[4] = {0x00, 0x50, 0xf2, 0x1};static u8 wpa2_oid[2] = {0x01, 0x00};/* Beacon/ProbeResp payload header */struct ieee80211_beacon_phdr {	u8 timestamp[8];	u16 beacon_int;	u16 capab_info;} __attribute__ ((packed));#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"/* Wireless extensions controls */static void format_event(struct net_device *dev, char *dest, const char *str,	     const struct obj_mlme *mlme, u16 *length, int error){	struct net_local *lp = dev->priv;		const u8 *a = mlme->address;	int n = snprintf(dest, IW_CUSTOM_MAX,			 "%s %s %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X %s (%2.2X)",			 str,			 ((lp->sm_mode == SM_MODE_AP) ? "from" : "to"),			 a[0], a[1], a[2], a[3], a[4], a[5],			 (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")			  : ""), mlme->code);	BUG_ON(n > IW_CUSTOM_MAX);	*length = n;}void send_formatted_event(struct net_device *dev, const char *str,			  const struct obj_mlme *mlme, int error){	union iwreq_data wrqu;	wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_ATOMIC);	if (!wrqu.data.pointer)		return;	wrqu.data.length = 0;	format_event(dev, wrqu.data.pointer, str, mlme, &wrqu.data.length,		     error);	wireless_send_event(dev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);	kfree(wrqu.data.pointer);}void send_wpa_ie_event(struct net_device *dev, char * bss_addr, char * wpa_ie, size_t wpa_ie_len, uint32_t event){	union iwreq_data wrqu;	uint32_t we_event;	if (wpa_ie_len > IW_GENERIC_IE_MAX)		return;	switch (event) {	case DOT11_OID_ASSOCIATE:	case DOT11_OID_REASSOCIATE:		we_event = IWEVASSOCREQIE;		break;	case DOT11_STATE_ASSOC:		we_event = IWEVASSOCRESPIE;		break;	default:		printk(KERN_ERR "%s(): Unknown event 0x%x\n", __FUNCTION__, event);		return;	}	wrqu.data.pointer = kzalloc(ETH_ALEN + 1 + wpa_ie_len, GFP_ATOMIC);	if (!wrqu.data.pointer)		return;	memcpy(wrqu.data.pointer, bss_addr, ETH_ALEN);	*((char *)(wrqu.data.pointer + ETH_ALEN)) = ':';		memcpy(wrqu.data.pointer + ETH_ALEN + 1, wpa_ie, wpa_ie_len);		wrqu.data.length = ETH_ALEN + 1 + wpa_ie_len;	wireless_send_event(dev, we_event, &wrqu, wrqu.data.pointer);	kfree(wrqu.data.pointer);}void send_simple_event(struct net_device *dev, const char *str){	union iwreq_data wrqu;	int n = strlen(str);	wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_ATOMIC);	if (!wrqu.data.pointer)		return;	BUG_ON(n > IW_CUSTOM_MAX);	wrqu.data.length = n;	strcpy(wrqu.data.pointer, str);	wireless_send_event(dev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);	kfree(wrqu.data.pointer);}static size_t sm_drv_wpa_ie_get(struct net_device *dev, u8 *bssid, u8 *wpa_ie){	struct net_local *lp = dev->priv;	struct list_head *ptr;	struct sm_drv_bss_wpa_ie *bss = NULL;	size_t len = 0;	if (down_trylock(&lp->wpa_sem))		return 0;	list_for_each(ptr, &lp->bss_wpa_list) {		bss = list_entry(ptr, struct sm_drv_bss_wpa_ie, list);		if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)			break;		bss = NULL;	}	if (bss) {		len = bss->wpa_ie_len;		memcpy(wpa_ie, bss->wpa_ie, len);	}	up(&lp->wpa_sem);	return len;}/* a little helper that will translate our data into a card independent * format that the Wireless Tools will understand. This was inspired by * the "Aironet driver for 4500 and 4800 series cards" (GPL) */inline char * sm_drv_translate_bss(struct net_device *dev, char *current_ev,				   char *end_buf, struct obj_bssex *bss, char noise){	struct iw_event iwe;	/* Temporary buffer */	short cap;	u8 wpa_ie[MAX_WPA_IE_LEN];	size_t wpa_ie_len;	/* The first entry must be the MAC address */	memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN);	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;	iwe.cmd = SIOCGIWAP;	current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);	/* The following entries will be displayed in the same order we give them */	/* The ESSID. */	iwe.u.data.length = strlen(bss->ssid);	iwe.u.data.flags = 1;	iwe.cmd = SIOCGIWESSID;	current_ev = iwe_stream_add_point(current_ev, end_buf,					  &iwe, (bss->ssid + 1));	/* Capabilities */#define CAP_ESS 0x01#define CAP_IBSS 0x02#define CAP_CRYPT 0x10	/* Mode */	cap = bss->capinfo;	iwe.u.mode = 0;	if (cap & CAP_ESS)		iwe.u.mode = IW_MODE_MASTER;	else if (cap & CAP_IBSS)		iwe.u.mode = IW_MODE_ADHOC;	iwe.cmd = SIOCGIWMODE;	if (iwe.u.mode)		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,						  IW_EV_UINT_LEN);	/* Encryption capability */	if (cap & CAP_CRYPT)		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;	else		iwe.u.data.flags = IW_ENCODE_DISABLED;	iwe.u.data.length = 0;	iwe.cmd = SIOCGIWENCODE;	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);	/* Add frequency. (short) bss->channel is the frequency in MHz */	iwe.u.freq.m = channel_of_freq(bss->channel);	iwe.u.freq.e = 0;	iwe.cmd = SIOCGIWFREQ;	current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);	/* Add quality statistics */	iwe.u.qual.level = bss->rssi;	iwe.u.qual.noise = noise;	/* do a simple SNR for quality */	iwe.u.qual.qual = bss->rssi - noise;	iwe.cmd = IWEVQUAL;	current_ev =	    iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);	wpa_ie_len = sm_drv_wpa_ie_get(dev, bss->address, wpa_ie);	if (wpa_ie_len > 0 && wpa_ie_len < MAX_WPA_IE_LEN) {		memset(&iwe, 0, sizeof(iwe));                iwe.cmd = IWEVGENIE;                iwe.u.data.length = wpa_ie_len;                current_ev = iwe_stream_add_point(                        current_ev, end_buf, &iwe, wpa_ie);			}	return current_ev;}int sm_drv_parse_bssid(struct net_device *dev, uint8_t * mac_filter, uint32_t event, char** event_stream){	struct obj_bssex * bssex, * first_bssex;		char * first_ev = NULL;	uint32_t noise = 0;	int ret;	if (event_stream) {		first_ev = *event_stream;		ret = sm_drv_oid_get(dev, DOT11_OID_NOISEFLOOR, (void*)&noise, sizeof(uint32_t));			if (ret < 0)			goto out;	}#define MAX_PROBE_LENGTH 256		bssex = (struct obj_bssex *)kzalloc(sizeof(struct obj_bssex) + MAX_PROBE_LENGTH,					    GFP_ATOMIC);	if (bssex == NULL) {		ret = -ENOMEM;		goto out;	}		first_bssex = (struct obj_bssex *)kzalloc(sizeof(struct obj_bssex) + MAX_PROBE_LENGTH,						  GFP_ATOMIC);	if (first_bssex == NULL) {		ret = -ENOMEM;		goto out_free_first;	}	/* 	 * First we need to get a beacon frame for all APs, and	 * build the wpa list.	 */	ret = sm_drv_oid_set(dev, DOT11_OID_BSSITERATE, (void*)bssex, sizeof(struct obj_bssex));	if (ret < 0)		goto out_free;	ret = sm_drv_oid_get(dev, DOT11_OID_BSSITERATE, (void*)first_bssex, sizeof(struct obj_bssex) + MAX_PROBE_LENGTH);	if (ret < 0)		goto out_free;		if ((mac_filter != NULL && !memcmp(first_bssex->address, mac_filter, ETH_ALEN)) ||	    mac_filter == NULL) {		ret = sm_drv_process_bss_data(dev, first_bssex->address, first_bssex->data, first_bssex->length, event);		/* We have found one and this is the one we were looking for */		if (ret == 0 &&		    (mac_filter != NULL && !memcmp(first_bssex->address, mac_filter, ETH_ALEN)))			goto out_free;	}		if (event_stream) {		*event_stream = sm_drv_translate_bss(dev, *event_stream,						     first_ev + IW_SCAN_MAX_DATA,						     first_bssex, noise);	}		/* BSSITERATE loops forever... */	do {		bssex->length = 0;		ret = sm_drv_oid_get(dev, DOT11_OID_BSSITERATE, (void*)bssex, sizeof(struct obj_bssex) + MAX_PROBE_LENGTH);		if (ret < 0)			goto out_free;		/*		 * We're done whenever the length is 0,		 * or when we're rolling back to the first bssid.		 */		if (!bssex->length ||		    !memcmp(first_bssex->address, bssex->address, ETH_ALEN))			break;				if ((mac_filter != NULL && !memcmp(bssex->address, mac_filter, ETH_ALEN)) ||		    mac_filter == NULL) {			ret = sm_drv_process_bss_data(dev, bssex->address, bssex->data, bssex->length, event);			/* We have found one and this is the one we were looking for */			if (ret == 0 &&			    (mac_filter != NULL && !memcmp(bssex->address, mac_filter, ETH_ALEN)))				goto out_free;		}				if (event_stream) {			*event_stream = sm_drv_translate_bss(dev, *event_stream,							     first_ev + IW_SCAN_MAX_DATA,							     bssex, noise);		}	} while(memcmp(first_bssex->address, bssex->address, ETH_ALEN));	ret = sm_drv_oid_set(dev, DOT11_OID_BSSITERATE, (void*)bssex, sizeof(struct obj_bssex));	if (ret < 0)		goto out_free; out_free:		kfree(first_bssex); out_free_first:	kfree(bssex); out:	return ret;}/* In case we don't get a trap from the UMAC...*/void send_scan_complete_timer(unsigned long handle){	struct net_device *dev = (struct net_device *)handle;	struct net_local *lp = dev->priv;	union iwreq_data wrqu;	uint32_t target_ints = SPI_TARGET_INT_RDDONE;	uint32_t bgr_scan_disable = 0;		printk("SCAN timer expired, resetting WLAN\n");	sm_spi_write(dev, SPI_ADRS_ARM_INTERRUPTS, (unsigned char *)&target_ints, sizeof(target_ints));		wrqu.data.length = 0;	wrqu.data.flags = 0;		if (lp->bss_type != DOT11_BSSTYPE_IBSS)		sm_drv_oid_set(dev, DOT11_OID_AUTOSCANDISABLE, 			       (void*)&bgr_scan_disable, sizeof(uint32_t));		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);}void send_scan_complete(struct net_device *dev){	union iwreq_data wrqu;		struct net_local *lp = dev->priv;	wrqu.data.length = 0;	wrqu.data.flags = 0;		/* We parse the BSSIDs, but we don't send the event */	sm_drv_parse_bssid(dev, NULL, 0, NULL);	if (lp->link_state != DOT11_STATE_ASSOC) {		uint32_t bgr_scan_disable = 0;		sm_drv_oid_set(dev, DOT11_OID_AUTOSCANDISABLE, 			       (void*)&bgr_scan_disable, sizeof(uint32_t));	}		wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);}void send_mic_failure(struct net_device *dev, struct obj_sta *sta){	union iwreq_data wrqu;	struct iw_michaelmicfailure micfailure;	struct obj_stasc sta_seqcounter;	char * p;		/* Who is the guilty guy ? */	memcpy(micfailure.src_addr.sa_data, sta->address, ETH_ALEN);	micfailure.src_addr.sa_family = ARPHRD_ETHER;	memcpy(sta_seqcounter.address, sta->address, ETH_ALEN);		/* We need to report the sequence counter as well */	if (sm_drv_oid_get(dev, DOT11_OID_STASC, (void*)&sta_seqcounter, sizeof(struct obj_stasc)) < 0)		memset(micfailure.tsc, 0, IW_ENCODE_SEQ_MAX_SIZE);	else {		memcpy(micfailure.tsc, &(sta_seqcounter.sc_low), 2);		memcpy(micfailure.tsc + 2, &(sta_seqcounter.sc_high), 4);	}		wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_ATOMIC);	if (!wrqu.data.pointer)		return;	p = wrqu.data.pointer;	/* We will be able to send a standard event with WE-18 */	p += sprintf(wrqu.data.pointer, "MIC_FAILURE: ");	p += snprintf(p, sizeof(struct iw_michaelmicfailure), "%s", (char*)&micfailure);	wrqu.data.length = p - (char *)wrqu.data.pointer;	wireless_send_event(dev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);	kfree(wrqu.data.pointer);}static int sm_drv_get_wap(struct net_device *dev, struct iw_request_info *info,			  struct sockaddr *awrq, char *extra);static int sm_drv_get_mode(struct net_device *dev, struct iw_request_info *info,			   __u32 * uwrq, char *extra);void link_changed(struct net_device *dev, u32 bitrate){	struct net_local *lp = dev->priv;	if (driver_type == SM_DRIVER_TYPE_UMAC) {		if (bitrate) {						if (lp->sm_mode == SM_MODE_CLIENT) {				union iwreq_data uwrq;				uint32_t mode;				struct obj_ssid ssid;				sm_drv_get_mode(dev, NULL, &mode, NULL);				if (mode == IW_MODE_INFRA) {					sm_drv_get_wap(dev, NULL, (struct sockaddr *) &uwrq,						       NULL);					sm_drv_oid_get(dev, DOT11_OID_SSID, (void*)&ssid, 						       sizeof(struct obj_ssid));					/* We can copy the AP MAC address */					spin_lock_bh(&lp->sm_lock);					memcpy(lp->ap_mac_address, ((struct sockaddr *)&uwrq)->sa_data, ETH_ALEN);					memcpy(&lp->ssid, &ssid, sizeof(struct obj_ssid));					spin_unlock_bh(&lp->sm_lock);										wireless_send_event(dev, SIOCGIWAP, &uwrq, NULL);				}			}		} else {			union iwreq_data uwrq;			uint32_t mode;						/* Association has been lost */			spin_lock_bh(&lp->sm_lock);			lp->link_state = DOT11_STATE_NONE;			memset(&lp->ssid, 0, sizeof(struct obj_ssid));			spin_unlock_bh(&lp->sm_lock);						sm_drv_get_mode(dev, NULL, &mode, NULL);						if (mode == IW_MODE_INFRA) {				uint32_t profile;				sm_drv_get_wap(dev, NULL, (struct sockaddr *) &uwrq,					       NULL);								wireless_send_event(dev, SIOCGIWAP, &uwrq, NULL);								/* We get back to MIXED profile (it could have been changed for Nokia AP) */				sm_drv_oid_get(dev, DOT11_OID_PROFILES, (void*)&profile, sizeof(uint32_t));				if (profile == DOT11_PROFILE_B_WIFI) {					profile = DOT11_PROFILE_MIXED;					sm_drv_oid_set(dev, DOT11_OID_PROFILES, (void*)&profile, sizeof(uint32_t));				}			}		}			}}static void sm_drv_wpa_ie_add(struct net_device *dev, uint8_t *bssid,			      uint8_t *wpa_ie, size_t wpa_ie_len, uint32_t event){	struct net_local *lp = dev->priv;	struct sm_drv_bss_wpa_ie *bss = NULL, *safe;	if (wpa_ie_len > MAX_WPA_IE_LEN)		wpa_ie_len = MAX_WPA_IE_LEN;	if (down_trylock(&lp->wpa_sem))		return;				/* We send the event after parsing the association frame */	if ((lp->link_state == DOT11_STATE_ASSOCING || lp->link_state == DOT11_STATE_ASSOC)  	    && event)		send_wpa_ie_event(dev, bssid, wpa_ie, wpa_ie_len, event);				/* try to use existing entry */	list_for_each_entry_safe(bss, safe, &lp->bss_wpa_list, list) {		if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {			/* We update the existing entry, IE can change with AP settings */			memset(bss->wpa_ie, 0, bss->wpa_ie_len);			memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);			memcpy(bss->bssid, bssid, ETH_ALEN);			bss->wpa_ie_len = wpa_ie_len;			list_move(&bss->list, &lp->bss_wpa_list);			bss->last_update = jiffies;			goto out;		}	}		/* Add a new BSS entry; if max number of entries is already	 * reached, replace the least recently updated */	if (lp->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {		printk("Reached maximum number of APs: %d\n", lp->num_bss_wpa);		bss = list_entry(lp->bss_wpa_list.prev,				 struct sm_drv_bss_wpa_ie, list);		list_del(&bss->list);		kfree(bss);		lp->num_bss_wpa--;	}		bss = kmalloc(sizeof (struct sm_drv_bss_wpa_ie), 		      GFP_ATOMIC);	if (bss != NULL) {		lp->num_bss_wpa++;		memset(bss, 0, sizeof (struct sm_drv_bss_wpa_ie));		memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);		memcpy(bss->bssid, bssid, ETH_ALEN);		bss->wpa_ie_len = wpa_ie_len;		bss->last_update = jiffies;		INIT_LIST_HEAD(&bss->list);		list_add(&bss->list, &lp->bss_wpa_list);	} else {		printk(KERN_DEBUG "Failed to add BSS WPA entry for " MACSTR		       "\n", MAC2STR(bssid));	} out:	/* expire old entries from WPA list */	list_for_each_entry_safe(bss, safe, &lp->bss_wpa_list, list) {		if (time_after(jiffies, bss->last_update + 60 * HZ)) {			DEBUG(DBG_WPA, "Haven't got any beacons from " MACSTR ", removing...\n", MAC2STR(bss->bssid));			list_del(&(bss->list));			lp->num_bss_wpa--;			kfree(bss);		}	}	up(&lp->wpa_sem);}static void sm_drv_wpa_ie_remove(struct net_device *dev, uint8_t *bssid){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -