📄 sm_drv_ioctl_umac.c
字号:
/* * 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 + -