18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Host AP (software wireless LAN access point) driver for 48c2ecf20Sopenharmony_ci * Intersil Prism2/2.5/3 - hostap.o module, common routines 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen 78c2ecf20Sopenharmony_ci * <j@w1.fi> 88c2ecf20Sopenharmony_ci * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 158c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/random.h> 188c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 198c2ecf20Sopenharmony_ci#include <linux/kmod.h> 208c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 218c2ecf20Sopenharmony_ci#include <linux/wireless.h> 228c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 238c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 248c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 258c2ecf20Sopenharmony_ci#include <net/lib80211.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "hostap_wlan.h" 298c2ecf20Sopenharmony_ci#include "hostap_80211.h" 308c2ecf20Sopenharmony_ci#include "hostap_ap.h" 318c2ecf20Sopenharmony_ci#include "hostap.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen"); 348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Host AP common routines"); 358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define TX_TIMEOUT (2 * HZ) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define PRISM2_MAX_FRAME_SIZE 2304 408c2ecf20Sopenharmony_ci#define PRISM2_MIN_MTU 256 418c2ecf20Sopenharmony_ci/* FIX: */ 428c2ecf20Sopenharmony_ci#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */)) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct net_device * hostap_add_interface(struct local_info *local, 468c2ecf20Sopenharmony_ci int type, int rtnl_locked, 478c2ecf20Sopenharmony_ci const char *prefix, 488c2ecf20Sopenharmony_ci const char *name) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct net_device *dev, *mdev; 518c2ecf20Sopenharmony_ci struct hostap_interface *iface; 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct hostap_interface)); 558c2ecf20Sopenharmony_ci if (dev == NULL) 568c2ecf20Sopenharmony_ci return NULL; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 598c2ecf20Sopenharmony_ci iface->dev = dev; 608c2ecf20Sopenharmony_ci iface->local = local; 618c2ecf20Sopenharmony_ci iface->type = type; 628c2ecf20Sopenharmony_ci list_add(&iface->list, &local->hostap_interfaces); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci mdev = local->dev; 658c2ecf20Sopenharmony_ci eth_hw_addr_inherit(dev, mdev); 668c2ecf20Sopenharmony_ci dev->base_addr = mdev->base_addr; 678c2ecf20Sopenharmony_ci dev->irq = mdev->irq; 688c2ecf20Sopenharmony_ci dev->mem_start = mdev->mem_start; 698c2ecf20Sopenharmony_ci dev->mem_end = mdev->mem_end; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci hostap_setup_dev(dev, local, type); 728c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci sprintf(dev->name, "%s%s", prefix, name); 758c2ecf20Sopenharmony_ci if (!rtnl_locked) 768c2ecf20Sopenharmony_ci rtnl_lock(); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, mdev->dev.parent); 798c2ecf20Sopenharmony_ci ret = register_netdevice(dev); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!rtnl_locked) 828c2ecf20Sopenharmony_ci rtnl_unlock(); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (ret < 0) { 858c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: failed to add new netdevice!\n", 868c2ecf20Sopenharmony_ci dev->name); 878c2ecf20Sopenharmony_ci free_netdev(dev); 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: registered netdevice %s\n", 928c2ecf20Sopenharmony_ci mdev->name, dev->name); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return dev; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_civoid hostap_remove_interface(struct net_device *dev, int rtnl_locked, 998c2ecf20Sopenharmony_ci int remove_from_list) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct hostap_interface *iface; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!dev) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (remove_from_list) { 1098c2ecf20Sopenharmony_ci list_del(&iface->list); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (dev == iface->local->ddev) 1138c2ecf20Sopenharmony_ci iface->local->ddev = NULL; 1148c2ecf20Sopenharmony_ci else if (dev == iface->local->apdev) 1158c2ecf20Sopenharmony_ci iface->local->apdev = NULL; 1168c2ecf20Sopenharmony_ci else if (dev == iface->local->stadev) 1178c2ecf20Sopenharmony_ci iface->local->stadev = NULL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (rtnl_locked) 1208c2ecf20Sopenharmony_ci unregister_netdevice(dev); 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci unregister_netdev(dev); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 'dev->needs_free_netdev = true' implies device data, including 1258c2ecf20Sopenharmony_ci * private data, will be freed when the device is removed */ 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic inline int prism2_wds_special_addr(u8 *addr) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5]) 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 1; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciint prism2_wds_add(local_info_t *local, u8 *remote_addr, 1398c2ecf20Sopenharmony_ci int rtnl_locked) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct net_device *dev; 1428c2ecf20Sopenharmony_ci struct list_head *ptr; 1438c2ecf20Sopenharmony_ci struct hostap_interface *iface, *empty, *match; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci empty = match = NULL; 1468c2ecf20Sopenharmony_ci read_lock_bh(&local->iface_lock); 1478c2ecf20Sopenharmony_ci list_for_each(ptr, &local->hostap_interfaces) { 1488c2ecf20Sopenharmony_ci iface = list_entry(ptr, struct hostap_interface, list); 1498c2ecf20Sopenharmony_ci if (iface->type != HOSTAP_INTERFACE_WDS) 1508c2ecf20Sopenharmony_ci continue; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (prism2_wds_special_addr(iface->u.wds.remote_addr)) 1538c2ecf20Sopenharmony_ci empty = iface; 1548c2ecf20Sopenharmony_ci else if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { 1558c2ecf20Sopenharmony_ci match = iface; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci if (!match && empty && !prism2_wds_special_addr(remote_addr)) { 1608c2ecf20Sopenharmony_ci /* take pre-allocated entry into use */ 1618c2ecf20Sopenharmony_ci memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN); 1628c2ecf20Sopenharmony_ci read_unlock_bh(&local->iface_lock); 1638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", 1648c2ecf20Sopenharmony_ci local->dev->name, empty->dev->name); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci read_unlock_bh(&local->iface_lock); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!prism2_wds_special_addr(remote_addr)) { 1708c2ecf20Sopenharmony_ci if (match) 1718c2ecf20Sopenharmony_ci return -EEXIST; 1728c2ecf20Sopenharmony_ci hostap_add_sta(local->ap, remote_addr); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (local->wds_connections >= local->wds_max_connections) 1768c2ecf20Sopenharmony_ci return -ENOBUFS; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* verify that there is room for wds# postfix in the interface name */ 1798c2ecf20Sopenharmony_ci if (strlen(local->dev->name) >= IFNAMSIZ - 5) { 1808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "'%s' too long base device name\n", 1818c2ecf20Sopenharmony_ci local->dev->name); 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked, 1868c2ecf20Sopenharmony_ci local->ddev->name, "wds%d"); 1878c2ecf20Sopenharmony_ci if (dev == NULL) 1888c2ecf20Sopenharmony_ci return -ENOMEM; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 1918c2ecf20Sopenharmony_ci memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci local->wds_connections++; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint prism2_wds_del(local_info_t *local, u8 *remote_addr, 2008c2ecf20Sopenharmony_ci int rtnl_locked, int do_not_remove) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci unsigned long flags; 2038c2ecf20Sopenharmony_ci struct list_head *ptr; 2048c2ecf20Sopenharmony_ci struct hostap_interface *iface, *selected = NULL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci write_lock_irqsave(&local->iface_lock, flags); 2078c2ecf20Sopenharmony_ci list_for_each(ptr, &local->hostap_interfaces) { 2088c2ecf20Sopenharmony_ci iface = list_entry(ptr, struct hostap_interface, list); 2098c2ecf20Sopenharmony_ci if (iface->type != HOSTAP_INTERFACE_WDS) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { 2138c2ecf20Sopenharmony_ci selected = iface; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci if (selected && !do_not_remove) 2188c2ecf20Sopenharmony_ci list_del(&selected->list); 2198c2ecf20Sopenharmony_ci write_unlock_irqrestore(&local->iface_lock, flags); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (selected) { 2228c2ecf20Sopenharmony_ci if (do_not_remove) 2238c2ecf20Sopenharmony_ci eth_zero_addr(selected->u.wds.remote_addr); 2248c2ecf20Sopenharmony_ci else { 2258c2ecf20Sopenharmony_ci hostap_remove_interface(selected->dev, rtnl_locked, 0); 2268c2ecf20Sopenharmony_ci local->wds_connections--; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return selected ? 0 : -ENODEV; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciu16 hostap_tx_callback_register(local_info_t *local, 2358c2ecf20Sopenharmony_ci void (*func)(struct sk_buff *, int ok, void *), 2368c2ecf20Sopenharmony_ci void *data) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned long flags; 2398c2ecf20Sopenharmony_ci struct hostap_tx_callback_info *entry; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 2428c2ecf20Sopenharmony_ci if (entry == NULL) 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci entry->func = func; 2468c2ecf20Sopenharmony_ci entry->data = data; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 2498c2ecf20Sopenharmony_ci entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1; 2508c2ecf20Sopenharmony_ci entry->next = local->tx_callback; 2518c2ecf20Sopenharmony_ci local->tx_callback = entry; 2528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return entry->idx; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciint hostap_tx_callback_unregister(local_info_t *local, u16 idx) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned long flags; 2618c2ecf20Sopenharmony_ci struct hostap_tx_callback_info *cb, *prev = NULL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->lock, flags); 2648c2ecf20Sopenharmony_ci cb = local->tx_callback; 2658c2ecf20Sopenharmony_ci while (cb != NULL && cb->idx != idx) { 2668c2ecf20Sopenharmony_ci prev = cb; 2678c2ecf20Sopenharmony_ci cb = cb->next; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci if (cb) { 2708c2ecf20Sopenharmony_ci if (prev == NULL) 2718c2ecf20Sopenharmony_ci local->tx_callback = cb->next; 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci prev->next = cb->next; 2748c2ecf20Sopenharmony_ci kfree(cb); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->lock, flags); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return cb ? 0 : -1; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* val is in host byte order */ 2838c2ecf20Sopenharmony_ciint hostap_set_word(struct net_device *dev, int rid, u16 val) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct hostap_interface *iface; 2868c2ecf20Sopenharmony_ci __le16 tmp = cpu_to_le16(val); 2878c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 2888c2ecf20Sopenharmony_ci return iface->local->func->set_rid(dev, rid, &tmp, 2); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciint hostap_set_string(struct net_device *dev, int rid, const char *val) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct hostap_interface *iface; 2958c2ecf20Sopenharmony_ci char buf[MAX_SSID_LEN + 2]; 2968c2ecf20Sopenharmony_ci int len; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 2998c2ecf20Sopenharmony_ci len = strlen(val); 3008c2ecf20Sopenharmony_ci if (len > MAX_SSID_LEN) 3018c2ecf20Sopenharmony_ci return -1; 3028c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 3038c2ecf20Sopenharmony_ci buf[0] = len; /* little endian 16 bit word */ 3048c2ecf20Sopenharmony_ci memcpy(buf + 2, val, len); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciu16 hostap_get_porttype(local_info_t *local) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc) 3138c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_PSEUDO_IBSS; 3148c2ecf20Sopenharmony_ci if (local->iw_mode == IW_MODE_ADHOC) 3158c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_IBSS; 3168c2ecf20Sopenharmony_ci if (local->iw_mode == IW_MODE_INFRA) 3178c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_BSS; 3188c2ecf20Sopenharmony_ci if (local->iw_mode == IW_MODE_REPEAT) 3198c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_WDS; 3208c2ecf20Sopenharmony_ci if (local->iw_mode == IW_MODE_MONITOR) 3218c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_PSEUDO_IBSS; 3228c2ecf20Sopenharmony_ci return HFA384X_PORTTYPE_HOSTAP; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ciint hostap_set_encryption(local_info_t *local) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci u16 val, old_val; 3298c2ecf20Sopenharmony_ci int i, keylen, len, idx; 3308c2ecf20Sopenharmony_ci char keybuf[WEP_KEY_LEN + 1]; 3318c2ecf20Sopenharmony_ci enum { NONE, WEP, OTHER } encrypt_type; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci idx = local->crypt_info.tx_keyidx; 3348c2ecf20Sopenharmony_ci if (local->crypt_info.crypt[idx] == NULL || 3358c2ecf20Sopenharmony_ci local->crypt_info.crypt[idx]->ops == NULL) 3368c2ecf20Sopenharmony_ci encrypt_type = NONE; 3378c2ecf20Sopenharmony_ci else if (strcmp(local->crypt_info.crypt[idx]->ops->name, "WEP") == 0) 3388c2ecf20Sopenharmony_ci encrypt_type = WEP; 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci encrypt_type = OTHER; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 3438c2ecf20Sopenharmony_ci 1) < 0) { 3448c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Could not read current WEP flags.\n"); 3458c2ecf20Sopenharmony_ci goto fail; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci le16_to_cpus(&val); 3488c2ecf20Sopenharmony_ci old_val = val; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (encrypt_type != NONE || local->privacy_invoked) 3518c2ecf20Sopenharmony_ci val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (local->open_wep || encrypt_type == NONE || 3568c2ecf20Sopenharmony_ci ((local->ieee_802_1x || local->wpa) && local->host_decrypt)) 3578c2ecf20Sopenharmony_ci val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if ((encrypt_type != NONE || local->privacy_invoked) && 3628c2ecf20Sopenharmony_ci (encrypt_type == OTHER || local->host_encrypt)) 3638c2ecf20Sopenharmony_ci val |= HFA384X_WEPFLAGS_HOSTENCRYPT; 3648c2ecf20Sopenharmony_ci else 3658c2ecf20Sopenharmony_ci val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; 3668c2ecf20Sopenharmony_ci if ((encrypt_type != NONE || local->privacy_invoked) && 3678c2ecf20Sopenharmony_ci (encrypt_type == OTHER || local->host_decrypt)) 3688c2ecf20Sopenharmony_ci val |= HFA384X_WEPFLAGS_HOSTDECRYPT; 3698c2ecf20Sopenharmony_ci else 3708c2ecf20Sopenharmony_ci val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (val != old_val && 3738c2ecf20Sopenharmony_ci hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) { 3748c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n", 3758c2ecf20Sopenharmony_ci val); 3768c2ecf20Sopenharmony_ci goto fail; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (encrypt_type != WEP) 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 104-bit support seems to require that all the keys are set to the 3838c2ecf20Sopenharmony_ci * same keylen */ 3848c2ecf20Sopenharmony_ci keylen = 6; /* first 5 octets */ 3858c2ecf20Sopenharmony_ci len = local->crypt_info.crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), NULL, 3868c2ecf20Sopenharmony_ci local->crypt_info.crypt[idx]->priv); 3878c2ecf20Sopenharmony_ci if (idx >= 0 && idx < WEP_KEYS && len > 5) 3888c2ecf20Sopenharmony_ci keylen = WEP_KEY_LEN + 1; /* first 13 octets */ 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci for (i = 0; i < WEP_KEYS; i++) { 3918c2ecf20Sopenharmony_ci memset(keybuf, 0, sizeof(keybuf)); 3928c2ecf20Sopenharmony_ci if (local->crypt_info.crypt[i]) { 3938c2ecf20Sopenharmony_ci (void) local->crypt_info.crypt[i]->ops->get_key( 3948c2ecf20Sopenharmony_ci keybuf, sizeof(keybuf), 3958c2ecf20Sopenharmony_ci NULL, local->crypt_info.crypt[i]->priv); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci if (local->func->set_rid(local->dev, 3988c2ecf20Sopenharmony_ci HFA384X_RID_CNFDEFAULTKEY0 + i, 3998c2ecf20Sopenharmony_ci keybuf, keylen)) { 4008c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Could not set key %d (len=%d)\n", 4018c2ecf20Sopenharmony_ci i, keylen); 4028c2ecf20Sopenharmony_ci goto fail; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) { 4068c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Could not set default keyid %d\n", idx); 4078c2ecf20Sopenharmony_ci goto fail; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci fail: 4138c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name); 4148c2ecf20Sopenharmony_ci return -1; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ciint hostap_set_antsel(local_info_t *local) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci u16 val; 4218c2ecf20Sopenharmony_ci int ret = 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH && 4248c2ecf20Sopenharmony_ci local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, 4258c2ecf20Sopenharmony_ci HFA386X_CR_TX_CONFIGURE, 4268c2ecf20Sopenharmony_ci NULL, &val) == 0) { 4278c2ecf20Sopenharmony_ci val &= ~(BIT(2) | BIT(1)); 4288c2ecf20Sopenharmony_ci switch (local->antsel_tx) { 4298c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_DIVERSITY: 4308c2ecf20Sopenharmony_ci val |= BIT(1); 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_LOW: 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_HIGH: 4358c2ecf20Sopenharmony_ci val |= BIT(2); 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, 4408c2ecf20Sopenharmony_ci HFA386X_CR_TX_CONFIGURE, &val, NULL)) { 4418c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: setting TX AntSel failed\n", 4428c2ecf20Sopenharmony_ci local->dev->name); 4438c2ecf20Sopenharmony_ci ret = -1; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH && 4488c2ecf20Sopenharmony_ci local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, 4498c2ecf20Sopenharmony_ci HFA386X_CR_RX_CONFIGURE, 4508c2ecf20Sopenharmony_ci NULL, &val) == 0) { 4518c2ecf20Sopenharmony_ci val &= ~(BIT(1) | BIT(0)); 4528c2ecf20Sopenharmony_ci switch (local->antsel_rx) { 4538c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_DIVERSITY: 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_LOW: 4568c2ecf20Sopenharmony_ci val |= BIT(0); 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case HOSTAP_ANTSEL_HIGH: 4598c2ecf20Sopenharmony_ci val |= BIT(0) | BIT(1); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, 4648c2ecf20Sopenharmony_ci HFA386X_CR_RX_CONFIGURE, &val, NULL)) { 4658c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: setting RX AntSel failed\n", 4668c2ecf20Sopenharmony_ci local->dev->name); 4678c2ecf20Sopenharmony_ci ret = -1; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ciint hostap_set_roaming(local_info_t *local) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci u16 val; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci switch (local->host_roaming) { 4808c2ecf20Sopenharmony_ci case 1: 4818c2ecf20Sopenharmony_ci val = HFA384X_ROAMING_HOST; 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci case 2: 4848c2ecf20Sopenharmony_ci val = HFA384X_ROAMING_DISABLED; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci case 0: 4878c2ecf20Sopenharmony_ci default: 4888c2ecf20Sopenharmony_ci val = HFA384X_ROAMING_FIRMWARE; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciint hostap_set_auth_algs(local_info_t *local) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci int val = local->auth_algs; 4998c2ecf20Sopenharmony_ci /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication 5008c2ecf20Sopenharmony_ci * set to include both Open and Shared Key flags. It tries to use 5018c2ecf20Sopenharmony_ci * Shared Key authentication in that case even if WEP keys are not 5028c2ecf20Sopenharmony_ci * configured.. STA f/w v0.7.6 is able to handle such configuration, 5038c2ecf20Sopenharmony_ci * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */ 5048c2ecf20Sopenharmony_ci if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) && 5058c2ecf20Sopenharmony_ci val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY) 5068c2ecf20Sopenharmony_ci val = PRISM2_AUTH_OPEN; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) { 5098c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x " 5108c2ecf20Sopenharmony_ci "failed\n", local->dev->name, local->auth_algs); 5118c2ecf20Sopenharmony_ci return -EINVAL; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_civoid hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci u16 status, fc; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci status = __le16_to_cpu(rx->status); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, " 5258c2ecf20Sopenharmony_ci "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; " 5268c2ecf20Sopenharmony_ci "jiffies=%ld\n", 5278c2ecf20Sopenharmony_ci name, status, (status >> 8) & 0x07, status >> 13, status & 1, 5288c2ecf20Sopenharmony_ci rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci fc = __le16_to_cpu(rx->frame_control); 5318c2ecf20Sopenharmony_ci printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " 5328c2ecf20Sopenharmony_ci "data_len=%d%s%s\n", 5338c2ecf20Sopenharmony_ci fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, 5348c2ecf20Sopenharmony_ci (fc & IEEE80211_FCTL_STYPE) >> 4, 5358c2ecf20Sopenharmony_ci __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl), 5368c2ecf20Sopenharmony_ci __le16_to_cpu(rx->data_len), 5378c2ecf20Sopenharmony_ci fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", 5388c2ecf20Sopenharmony_ci fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", 5418c2ecf20Sopenharmony_ci rx->addr1, rx->addr2, rx->addr3, rx->addr4); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", 5448c2ecf20Sopenharmony_ci rx->dst_addr, rx->src_addr, 5458c2ecf20Sopenharmony_ci __be16_to_cpu(rx->len)); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_civoid hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u16 fc; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d " 5548c2ecf20Sopenharmony_ci "tx_control=0x%04x; jiffies=%ld\n", 5558c2ecf20Sopenharmony_ci name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate, 5568c2ecf20Sopenharmony_ci __le16_to_cpu(tx->tx_control), jiffies); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci fc = __le16_to_cpu(tx->frame_control); 5598c2ecf20Sopenharmony_ci printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " 5608c2ecf20Sopenharmony_ci "data_len=%d%s%s\n", 5618c2ecf20Sopenharmony_ci fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, 5628c2ecf20Sopenharmony_ci (fc & IEEE80211_FCTL_STYPE) >> 4, 5638c2ecf20Sopenharmony_ci __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl), 5648c2ecf20Sopenharmony_ci __le16_to_cpu(tx->data_len), 5658c2ecf20Sopenharmony_ci fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", 5668c2ecf20Sopenharmony_ci fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", 5698c2ecf20Sopenharmony_ci tx->addr1, tx->addr2, tx->addr3, tx->addr4); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", 5728c2ecf20Sopenharmony_ci tx->dst_addr, tx->src_addr, 5738c2ecf20Sopenharmony_ci __be16_to_cpu(tx->len)); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int hostap_80211_header_parse(const struct sk_buff *skb, 5788c2ecf20Sopenharmony_ci unsigned char *haddr) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ 5818c2ecf20Sopenharmony_ci return ETH_ALEN; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ciint hostap_80211_get_hdrlen(__le16 fc) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci if (ieee80211_is_data(fc) && ieee80211_has_a4 (fc)) 5888c2ecf20Sopenharmony_ci return 30; /* Addr4 */ 5898c2ecf20Sopenharmony_ci else if (ieee80211_is_cts(fc) || ieee80211_is_ack(fc)) 5908c2ecf20Sopenharmony_ci return 10; 5918c2ecf20Sopenharmony_ci else if (ieee80211_is_ctl(fc)) 5928c2ecf20Sopenharmony_ci return 16; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 24; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int prism2_close(struct net_device *dev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct hostap_interface *iface; 6018c2ecf20Sopenharmony_ci local_info_t *local; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 6068c2ecf20Sopenharmony_ci local = iface->local; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (dev == local->ddev) { 6098c2ecf20Sopenharmony_ci prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT 6128c2ecf20Sopenharmony_ci if (!local->hostapd && dev == local->dev && 6138c2ecf20Sopenharmony_ci (!local->func->card_present || local->func->card_present(local)) && 6148c2ecf20Sopenharmony_ci local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER) 6158c2ecf20Sopenharmony_ci hostap_deauth_all_stas(dev, local->ap, 1); 6168c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (dev == local->dev) { 6198c2ecf20Sopenharmony_ci local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (netif_running(dev)) { 6238c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6248c2ecf20Sopenharmony_ci netif_device_detach(dev); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci cancel_work_sync(&local->reset_queue); 6288c2ecf20Sopenharmony_ci cancel_work_sync(&local->set_multicast_list_queue); 6298c2ecf20Sopenharmony_ci cancel_work_sync(&local->set_tim_queue); 6308c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_STATION_MODES 6318c2ecf20Sopenharmony_ci cancel_work_sync(&local->info_queue); 6328c2ecf20Sopenharmony_ci#endif 6338c2ecf20Sopenharmony_ci cancel_work_sync(&local->comms_qual_update); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci module_put(local->hw_module); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci local->num_dev_open--; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (dev != local->dev && local->dev->flags & IFF_UP && 6408c2ecf20Sopenharmony_ci local->master_dev_auto_open && local->num_dev_open == 1) { 6418c2ecf20Sopenharmony_ci /* Close master radio interface automatically if it was also 6428c2ecf20Sopenharmony_ci * opened automatically and we are now closing the last 6438c2ecf20Sopenharmony_ci * remaining non-master device. */ 6448c2ecf20Sopenharmony_ci dev_close(local->dev); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int prism2_open(struct net_device *dev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct hostap_interface *iface; 6548c2ecf20Sopenharmony_ci local_info_t *local; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 6598c2ecf20Sopenharmony_ci local = iface->local; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (local->no_pri) { 6628c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: could not set interface UP - no PRI " 6638c2ecf20Sopenharmony_ci "f/w\n", dev->name); 6648c2ecf20Sopenharmony_ci return -ENODEV; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if ((local->func->card_present && !local->func->card_present(local)) || 6688c2ecf20Sopenharmony_ci local->hw_downloading) 6698c2ecf20Sopenharmony_ci return -ENODEV; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (!try_module_get(local->hw_module)) 6728c2ecf20Sopenharmony_ci return -ENODEV; 6738c2ecf20Sopenharmony_ci local->num_dev_open++; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (!local->dev_enabled && local->func->hw_enable(dev, 1)) { 6768c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: could not enable MAC port\n", 6778c2ecf20Sopenharmony_ci dev->name); 6788c2ecf20Sopenharmony_ci prism2_close(dev); 6798c2ecf20Sopenharmony_ci return -ENODEV; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci if (!local->dev_enabled) 6828c2ecf20Sopenharmony_ci prism2_callback(local, PRISM2_CALLBACK_ENABLE); 6838c2ecf20Sopenharmony_ci local->dev_enabled = 1; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (dev != local->dev && !(local->dev->flags & IFF_UP)) { 6868c2ecf20Sopenharmony_ci /* Master radio interface is needed for all operation, so open 6878c2ecf20Sopenharmony_ci * it automatically when any virtual net_device is opened. */ 6888c2ecf20Sopenharmony_ci local->master_dev_auto_open = 1; 6898c2ecf20Sopenharmony_ci dev_open(local->dev, NULL); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci netif_device_attach(dev); 6938c2ecf20Sopenharmony_ci netif_start_queue(dev); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int prism2_set_mac_address(struct net_device *dev, void *p) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct hostap_interface *iface; 7028c2ecf20Sopenharmony_ci local_info_t *local; 7038c2ecf20Sopenharmony_ci struct list_head *ptr; 7048c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 7078c2ecf20Sopenharmony_ci local = iface->local; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, 7108c2ecf20Sopenharmony_ci ETH_ALEN) < 0 || local->func->reset_port(dev)) 7118c2ecf20Sopenharmony_ci return -EINVAL; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci read_lock_bh(&local->iface_lock); 7148c2ecf20Sopenharmony_ci list_for_each(ptr, &local->hostap_interfaces) { 7158c2ecf20Sopenharmony_ci iface = list_entry(ptr, struct hostap_interface, list); 7168c2ecf20Sopenharmony_ci memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN); 7198c2ecf20Sopenharmony_ci read_unlock_bh(&local->iface_lock); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/* TODO: to be further implemented as soon as Prism2 fully supports 7268c2ecf20Sopenharmony_ci * GroupAddresses and correct documentation is available */ 7278c2ecf20Sopenharmony_civoid hostap_set_multicast_list_queue(struct work_struct *work) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci local_info_t *local = 7308c2ecf20Sopenharmony_ci container_of(work, local_info_t, set_multicast_list_queue); 7318c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, 7348c2ecf20Sopenharmony_ci local->is_promisc)) { 7358c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %sabling promiscuous mode failed\n", 7368c2ecf20Sopenharmony_ci dev->name, local->is_promisc ? "en" : "dis"); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void hostap_set_multicast_list(struct net_device *dev) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci#if 0 7448c2ecf20Sopenharmony_ci /* FIX: promiscuous mode seems to be causing a lot of problems with 7458c2ecf20Sopenharmony_ci * some station firmware versions (FCSErr frames, invalid MACPort, etc. 7468c2ecf20Sopenharmony_ci * corrupted incoming frames). This code is now commented out while the 7478c2ecf20Sopenharmony_ci * problems are investigated. */ 7488c2ecf20Sopenharmony_ci struct hostap_interface *iface; 7498c2ecf20Sopenharmony_ci local_info_t *local; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 7528c2ecf20Sopenharmony_ci local = iface->local; 7538c2ecf20Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { 7548c2ecf20Sopenharmony_ci local->is_promisc = 1; 7558c2ecf20Sopenharmony_ci } else { 7568c2ecf20Sopenharmony_ci local->is_promisc = 0; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci schedule_work(&local->set_multicast_list_queue); 7608c2ecf20Sopenharmony_ci#endif 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic void prism2_tx_timeout(struct net_device *dev, unsigned int txqueue) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct hostap_interface *iface; 7678c2ecf20Sopenharmony_ci local_info_t *local; 7688c2ecf20Sopenharmony_ci struct hfa384x_regs regs; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 7718c2ecf20Sopenharmony_ci local = iface->local; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); 7748c2ecf20Sopenharmony_ci netif_stop_queue(local->dev); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci local->func->read_regs(dev, ®s); 7778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x " 7788c2ecf20Sopenharmony_ci "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n", 7798c2ecf20Sopenharmony_ci dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1, 7808c2ecf20Sopenharmony_ci regs.swsupport0); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci local->func->schedule_reset(local); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ciconst struct header_ops hostap_80211_ops = { 7868c2ecf20Sopenharmony_ci .create = eth_header, 7878c2ecf20Sopenharmony_ci .cache = eth_header_cache, 7888c2ecf20Sopenharmony_ci .cache_update = eth_header_cache_update, 7898c2ecf20Sopenharmony_ci .parse = hostap_80211_header_parse, 7908c2ecf20Sopenharmony_ci}; 7918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_80211_ops); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic const struct net_device_ops hostap_netdev_ops = { 7958c2ecf20Sopenharmony_ci .ndo_start_xmit = hostap_data_start_xmit, 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci .ndo_open = prism2_open, 7988c2ecf20Sopenharmony_ci .ndo_stop = prism2_close, 7998c2ecf20Sopenharmony_ci .ndo_do_ioctl = hostap_ioctl, 8008c2ecf20Sopenharmony_ci .ndo_set_mac_address = prism2_set_mac_address, 8018c2ecf20Sopenharmony_ci .ndo_set_rx_mode = hostap_set_multicast_list, 8028c2ecf20Sopenharmony_ci .ndo_tx_timeout = prism2_tx_timeout, 8038c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 8048c2ecf20Sopenharmony_ci}; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic const struct net_device_ops hostap_mgmt_netdev_ops = { 8078c2ecf20Sopenharmony_ci .ndo_start_xmit = hostap_mgmt_start_xmit, 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci .ndo_open = prism2_open, 8108c2ecf20Sopenharmony_ci .ndo_stop = prism2_close, 8118c2ecf20Sopenharmony_ci .ndo_do_ioctl = hostap_ioctl, 8128c2ecf20Sopenharmony_ci .ndo_set_mac_address = prism2_set_mac_address, 8138c2ecf20Sopenharmony_ci .ndo_set_rx_mode = hostap_set_multicast_list, 8148c2ecf20Sopenharmony_ci .ndo_tx_timeout = prism2_tx_timeout, 8158c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 8168c2ecf20Sopenharmony_ci}; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic const struct net_device_ops hostap_master_ops = { 8198c2ecf20Sopenharmony_ci .ndo_start_xmit = hostap_master_start_xmit, 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci .ndo_open = prism2_open, 8228c2ecf20Sopenharmony_ci .ndo_stop = prism2_close, 8238c2ecf20Sopenharmony_ci .ndo_do_ioctl = hostap_ioctl, 8248c2ecf20Sopenharmony_ci .ndo_set_mac_address = prism2_set_mac_address, 8258c2ecf20Sopenharmony_ci .ndo_set_rx_mode = hostap_set_multicast_list, 8268c2ecf20Sopenharmony_ci .ndo_tx_timeout = prism2_tx_timeout, 8278c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 8288c2ecf20Sopenharmony_ci}; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_civoid hostap_setup_dev(struct net_device *dev, local_info_t *local, 8318c2ecf20Sopenharmony_ci int type) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct hostap_interface *iface; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 8368c2ecf20Sopenharmony_ci ether_setup(dev); 8378c2ecf20Sopenharmony_ci dev->min_mtu = PRISM2_MIN_MTU; 8388c2ecf20Sopenharmony_ci dev->max_mtu = PRISM2_MAX_MTU; 8398c2ecf20Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* kernel callbacks */ 8428c2ecf20Sopenharmony_ci if (iface) { 8438c2ecf20Sopenharmony_ci /* Currently, we point to the proper spy_data only on 8448c2ecf20Sopenharmony_ci * the main_dev. This could be fixed. Jean II */ 8458c2ecf20Sopenharmony_ci iface->wireless_data.spy_data = &iface->spy_data; 8468c2ecf20Sopenharmony_ci dev->wireless_data = &iface->wireless_data; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci dev->wireless_handlers = &hostap_iw_handler_def; 8498c2ecf20Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci switch(type) { 8528c2ecf20Sopenharmony_ci case HOSTAP_INTERFACE_AP: 8538c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ 8548c2ecf20Sopenharmony_ci dev->netdev_ops = &hostap_mgmt_netdev_ops; 8558c2ecf20Sopenharmony_ci dev->type = ARPHRD_IEEE80211; 8568c2ecf20Sopenharmony_ci dev->header_ops = &hostap_80211_ops; 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci case HOSTAP_INTERFACE_MASTER: 8598c2ecf20Sopenharmony_ci dev->netdev_ops = &hostap_master_ops; 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci default: 8628c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ 8638c2ecf20Sopenharmony_ci dev->netdev_ops = &hostap_netdev_ops; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci dev->mtu = local->mtu; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci dev->ethtool_ops = &prism2_ethtool_ops; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (local->apdev) 8788c2ecf20Sopenharmony_ci return -EEXIST; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP, 8838c2ecf20Sopenharmony_ci rtnl_locked, local->ddev->name, 8848c2ecf20Sopenharmony_ci "ap"); 8858c2ecf20Sopenharmony_ci if (local->apdev == NULL) 8868c2ecf20Sopenharmony_ci return -ENOMEM; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic int hostap_disable_hostapd(local_info_t *local, int rtnl_locked) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci hostap_remove_interface(local->apdev, rtnl_locked, 1); 8998c2ecf20Sopenharmony_ci local->apdev = NULL; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (local->stadev) 9108c2ecf20Sopenharmony_ci return -EEXIST; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA, 9158c2ecf20Sopenharmony_ci rtnl_locked, local->ddev->name, 9168c2ecf20Sopenharmony_ci "sta"); 9178c2ecf20Sopenharmony_ci if (local->stadev == NULL) 9188c2ecf20Sopenharmony_ci return -ENOMEM; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci hostap_remove_interface(local->stadev, rtnl_locked, 1); 9318c2ecf20Sopenharmony_ci local->stadev = NULL; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ciint hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci int ret; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (val < 0 || val > 1) 9428c2ecf20Sopenharmony_ci return -EINVAL; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (local->hostapd == val) 9458c2ecf20Sopenharmony_ci return 0; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (val) { 9488c2ecf20Sopenharmony_ci ret = hostap_enable_hostapd(local, rtnl_locked); 9498c2ecf20Sopenharmony_ci if (ret == 0) 9508c2ecf20Sopenharmony_ci local->hostapd = 1; 9518c2ecf20Sopenharmony_ci } else { 9528c2ecf20Sopenharmony_ci local->hostapd = 0; 9538c2ecf20Sopenharmony_ci ret = hostap_disable_hostapd(local, rtnl_locked); 9548c2ecf20Sopenharmony_ci if (ret != 0) 9558c2ecf20Sopenharmony_ci local->hostapd = 1; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci return ret; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ciint hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci int ret; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (val < 0 || val > 1) 9678c2ecf20Sopenharmony_ci return -EINVAL; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (local->hostapd_sta == val) 9708c2ecf20Sopenharmony_ci return 0; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (val) { 9738c2ecf20Sopenharmony_ci ret = hostap_enable_hostapd_sta(local, rtnl_locked); 9748c2ecf20Sopenharmony_ci if (ret == 0) 9758c2ecf20Sopenharmony_ci local->hostapd_sta = 1; 9768c2ecf20Sopenharmony_ci } else { 9778c2ecf20Sopenharmony_ci local->hostapd_sta = 0; 9788c2ecf20Sopenharmony_ci ret = hostap_disable_hostapd_sta(local, rtnl_locked); 9798c2ecf20Sopenharmony_ci if (ret != 0) 9808c2ecf20Sopenharmony_ci local->hostapd_sta = 1; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci return ret; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ciint prism2_update_comms_qual(struct net_device *dev) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct hostap_interface *iface; 9918c2ecf20Sopenharmony_ci local_info_t *local; 9928c2ecf20Sopenharmony_ci int ret = 0; 9938c2ecf20Sopenharmony_ci struct hfa384x_comms_quality sq; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 9968c2ecf20Sopenharmony_ci local = iface->local; 9978c2ecf20Sopenharmony_ci if (!local->sta_fw_ver) 9988c2ecf20Sopenharmony_ci ret = -1; 9998c2ecf20Sopenharmony_ci else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { 10008c2ecf20Sopenharmony_ci if (local->func->get_rid(local->dev, 10018c2ecf20Sopenharmony_ci HFA384X_RID_DBMCOMMSQUALITY, 10028c2ecf20Sopenharmony_ci &sq, sizeof(sq), 1) >= 0) { 10038c2ecf20Sopenharmony_ci local->comms_qual = (s16) le16_to_cpu(sq.comm_qual); 10048c2ecf20Sopenharmony_ci local->avg_signal = (s16) le16_to_cpu(sq.signal_level); 10058c2ecf20Sopenharmony_ci local->avg_noise = (s16) le16_to_cpu(sq.noise_level); 10068c2ecf20Sopenharmony_ci local->last_comms_qual_update = jiffies; 10078c2ecf20Sopenharmony_ci } else 10088c2ecf20Sopenharmony_ci ret = -1; 10098c2ecf20Sopenharmony_ci } else { 10108c2ecf20Sopenharmony_ci if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY, 10118c2ecf20Sopenharmony_ci &sq, sizeof(sq), 1) >= 0) { 10128c2ecf20Sopenharmony_ci local->comms_qual = le16_to_cpu(sq.comm_qual); 10138c2ecf20Sopenharmony_ci local->avg_signal = HFA384X_LEVEL_TO_dBm( 10148c2ecf20Sopenharmony_ci le16_to_cpu(sq.signal_level)); 10158c2ecf20Sopenharmony_ci local->avg_noise = HFA384X_LEVEL_TO_dBm( 10168c2ecf20Sopenharmony_ci le16_to_cpu(sq.noise_level)); 10178c2ecf20Sopenharmony_ci local->last_comms_qual_update = jiffies; 10188c2ecf20Sopenharmony_ci } else 10198c2ecf20Sopenharmony_ci ret = -1; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ciint prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, 10278c2ecf20Sopenharmony_ci u8 *body, size_t bodylen) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct sk_buff *skb; 10308c2ecf20Sopenharmony_ci struct hostap_ieee80211_mgmt *mgmt; 10318c2ecf20Sopenharmony_ci struct hostap_skb_tx_data *meta; 10328c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen); 10358c2ecf20Sopenharmony_ci if (skb == NULL) 10368c2ecf20Sopenharmony_ci return -ENOMEM; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci mgmt = skb_put_zero(skb, IEEE80211_MGMT_HDR_LEN); 10398c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 10408c2ecf20Sopenharmony_ci memcpy(mgmt->da, dst, ETH_ALEN); 10418c2ecf20Sopenharmony_ci memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); 10428c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, dst, ETH_ALEN); 10438c2ecf20Sopenharmony_ci if (body) 10448c2ecf20Sopenharmony_ci skb_put_data(skb, body, bodylen); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci meta = (struct hostap_skb_tx_data *) skb->cb; 10478c2ecf20Sopenharmony_ci memset(meta, 0, sizeof(*meta)); 10488c2ecf20Sopenharmony_ci meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; 10498c2ecf20Sopenharmony_ci meta->iface = netdev_priv(dev); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci skb->dev = dev; 10528c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 10538c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 10548c2ecf20Sopenharmony_ci dev_queue_xmit(skb); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ciint prism2_sta_deauth(local_info_t *local, u16 reason) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci union iwreq_data wrqu; 10638c2ecf20Sopenharmony_ci int ret; 10648c2ecf20Sopenharmony_ci __le16 val = cpu_to_le16(reason); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (local->iw_mode != IW_MODE_INFRA || 10678c2ecf20Sopenharmony_ci is_zero_ether_addr(local->bssid) || 10688c2ecf20Sopenharmony_ci ether_addr_equal(local->bssid, "\x44\x44\x44\x44\x44\x44")) 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH, 10728c2ecf20Sopenharmony_ci (u8 *) &val, 2); 10738c2ecf20Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 10748c2ecf20Sopenharmony_ci wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cistruct proc_dir_entry *hostap_proc; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic int __init hostap_init(void) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci if (init_net.proc_net != NULL) { 10848c2ecf20Sopenharmony_ci hostap_proc = proc_mkdir("hostap", init_net.proc_net); 10858c2ecf20Sopenharmony_ci if (!hostap_proc) 10868c2ecf20Sopenharmony_ci printk(KERN_WARNING "Failed to mkdir " 10878c2ecf20Sopenharmony_ci "/proc/net/hostap\n"); 10888c2ecf20Sopenharmony_ci } else 10898c2ecf20Sopenharmony_ci hostap_proc = NULL; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic void __exit hostap_exit(void) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci if (hostap_proc != NULL) { 10988c2ecf20Sopenharmony_ci hostap_proc = NULL; 10998c2ecf20Sopenharmony_ci remove_proc_entry("hostap", init_net.proc_net); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_word); 11058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_string); 11068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_get_porttype); 11078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_encryption); 11088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_antsel); 11098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_roaming); 11108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_auth_algs); 11118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_dump_rx_header); 11128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_dump_tx_header); 11138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_80211_get_hdrlen); 11148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_setup_dev); 11158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_multicast_list_queue); 11168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_hostapd); 11178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_set_hostapd_sta); 11188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_add_interface); 11198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hostap_remove_interface); 11208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(prism2_update_comms_qual); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cimodule_init(hostap_init); 11238c2ecf20Sopenharmony_cimodule_exit(hostap_exit); 1124