18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for RNDIS based wireless USB devices. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net> 68c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@iki.fi> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Portions of this file are based on NDISwrapper project, 98c2ecf20Sopenharmony_ci * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani 108c2ecf20Sopenharmony_ci * http://ndiswrapper.sourceforge.net/ 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci// #define DEBUG // error path messages, extra info 148c2ecf20Sopenharmony_ci// #define VERBOSE // more; success messages 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 208c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/mii.h> 238c2ecf20Sopenharmony_ci#include <linux/usb.h> 248c2ecf20Sopenharmony_ci#include <linux/usb/cdc.h> 258c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 268c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 278c2ecf20Sopenharmony_ci#include <linux/ctype.h> 288c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 318c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 328c2ecf20Sopenharmony_ci#include <linux/usb/rndis_host.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* NOTE: All these are settings for Broadcom chipset */ 368c2ecf20Sopenharmony_cistatic char modparam_country[4] = "EU"; 378c2ecf20Sopenharmony_cimodule_param_string(country, modparam_country, 4, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int modparam_frameburst = 1; 418c2ecf20Sopenharmony_cimodule_param_named(frameburst, modparam_frameburst, int, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int modparam_afterburner = 0; 458c2ecf20Sopenharmony_cimodule_param_named(afterburner, modparam_afterburner, int, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(afterburner, 478c2ecf20Sopenharmony_ci "enable afterburner aka '125 High Speed Mode' (default: off)"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int modparam_power_save = 0; 508c2ecf20Sopenharmony_cimodule_param_named(power_save, modparam_power_save, int, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_save, 528c2ecf20Sopenharmony_ci "set power save mode: 0=off, 1=on, 2=fast (default: off)"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int modparam_power_output = 3; 558c2ecf20Sopenharmony_cimodule_param_named(power_output, modparam_power_output, int, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_output, 578c2ecf20Sopenharmony_ci "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int modparam_roamtrigger = -70; 608c2ecf20Sopenharmony_cimodule_param_named(roamtrigger, modparam_roamtrigger, int, 0444); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(roamtrigger, 628c2ecf20Sopenharmony_ci "set roaming dBm trigger: -80=optimize for distance, " 638c2ecf20Sopenharmony_ci "-60=bandwidth (default: -70)"); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int modparam_roamdelta = 1; 668c2ecf20Sopenharmony_cimodule_param_named(roamdelta, modparam_roamdelta, int, 0444); 678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(roamdelta, 688c2ecf20Sopenharmony_ci "set roaming tendency: 0=aggressive, 1=moderate, " 698c2ecf20Sopenharmony_ci "2=conservative (default: moderate)"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int modparam_workaround_interval; 728c2ecf20Sopenharmony_cimodule_param_named(workaround_interval, modparam_workaround_interval, 738c2ecf20Sopenharmony_ci int, 0444); 748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(workaround_interval, 758c2ecf20Sopenharmony_ci "set stall workaround interval in msecs (0=disabled) (default: 0)"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */ 788c2ecf20Sopenharmony_ci#define WL_NOISE -96 /* typical noise level in dBm */ 798c2ecf20Sopenharmony_ci#define WL_SIGMAX -32 /* typical maximum signal level in dBm */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Assume that Broadcom 4320 (only chipset at time of writing known to be 838c2ecf20Sopenharmony_ci * based on wireless rndis) has default txpower of 13dBm. 848c2ecf20Sopenharmony_ci * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. 858c2ecf20Sopenharmony_ci * 100% : 20 mW ~ 13dBm 868c2ecf20Sopenharmony_ci * 75% : 15 mW ~ 12dBm 878c2ecf20Sopenharmony_ci * 50% : 10 mW ~ 10dBm 888c2ecf20Sopenharmony_ci * 25% : 5 mW ~ 7dBm 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci#define BCM4320_DEFAULT_TXPOWER_DBM_100 13 918c2ecf20Sopenharmony_ci#define BCM4320_DEFAULT_TXPOWER_DBM_75 12 928c2ecf20Sopenharmony_ci#define BCM4320_DEFAULT_TXPOWER_DBM_50 10 938c2ecf20Sopenharmony_ci#define BCM4320_DEFAULT_TXPOWER_DBM_25 7 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Known device types */ 968c2ecf20Sopenharmony_ci#define RNDIS_UNKNOWN 0 978c2ecf20Sopenharmony_ci#define RNDIS_BCM4320A 1 988c2ecf20Sopenharmony_ci#define RNDIS_BCM4320B 2 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c 1028c2ecf20Sopenharmony_ci * slightly modified for datatype endianess, etc 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci#define NDIS_802_11_LENGTH_SSID 32 1058c2ecf20Sopenharmony_ci#define NDIS_802_11_LENGTH_RATES 8 1068c2ecf20Sopenharmony_ci#define NDIS_802_11_LENGTH_RATES_EX 16 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cienum ndis_80211_net_type { 1098c2ecf20Sopenharmony_ci NDIS_80211_TYPE_FREQ_HOP, 1108c2ecf20Sopenharmony_ci NDIS_80211_TYPE_DIRECT_SEQ, 1118c2ecf20Sopenharmony_ci NDIS_80211_TYPE_OFDM_A, 1128c2ecf20Sopenharmony_ci NDIS_80211_TYPE_OFDM_G 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cienum ndis_80211_net_infra { 1168c2ecf20Sopenharmony_ci NDIS_80211_INFRA_ADHOC, 1178c2ecf20Sopenharmony_ci NDIS_80211_INFRA_INFRA, 1188c2ecf20Sopenharmony_ci NDIS_80211_INFRA_AUTO_UNKNOWN 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cienum ndis_80211_auth_mode { 1228c2ecf20Sopenharmony_ci NDIS_80211_AUTH_OPEN, 1238c2ecf20Sopenharmony_ci NDIS_80211_AUTH_SHARED, 1248c2ecf20Sopenharmony_ci NDIS_80211_AUTH_AUTO_SWITCH, 1258c2ecf20Sopenharmony_ci NDIS_80211_AUTH_WPA, 1268c2ecf20Sopenharmony_ci NDIS_80211_AUTH_WPA_PSK, 1278c2ecf20Sopenharmony_ci NDIS_80211_AUTH_WPA_NONE, 1288c2ecf20Sopenharmony_ci NDIS_80211_AUTH_WPA2, 1298c2ecf20Sopenharmony_ci NDIS_80211_AUTH_WPA2_PSK 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cienum ndis_80211_encr_status { 1338c2ecf20Sopenharmony_ci NDIS_80211_ENCR_WEP_ENABLED, 1348c2ecf20Sopenharmony_ci NDIS_80211_ENCR_DISABLED, 1358c2ecf20Sopenharmony_ci NDIS_80211_ENCR_WEP_KEY_ABSENT, 1368c2ecf20Sopenharmony_ci NDIS_80211_ENCR_NOT_SUPPORTED, 1378c2ecf20Sopenharmony_ci NDIS_80211_ENCR_TKIP_ENABLED, 1388c2ecf20Sopenharmony_ci NDIS_80211_ENCR_TKIP_KEY_ABSENT, 1398c2ecf20Sopenharmony_ci NDIS_80211_ENCR_CCMP_ENABLED, 1408c2ecf20Sopenharmony_ci NDIS_80211_ENCR_CCMP_KEY_ABSENT 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cienum ndis_80211_priv_filter { 1448c2ecf20Sopenharmony_ci NDIS_80211_PRIV_ACCEPT_ALL, 1458c2ecf20Sopenharmony_ci NDIS_80211_PRIV_8021X_WEP 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cienum ndis_80211_status_type { 1498c2ecf20Sopenharmony_ci NDIS_80211_STATUSTYPE_AUTHENTICATION, 1508c2ecf20Sopenharmony_ci NDIS_80211_STATUSTYPE_MEDIASTREAMMODE, 1518c2ecf20Sopenharmony_ci NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST, 1528c2ecf20Sopenharmony_ci NDIS_80211_STATUSTYPE_RADIOSTATE, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cienum ndis_80211_media_stream_mode { 1568c2ecf20Sopenharmony_ci NDIS_80211_MEDIA_STREAM_OFF, 1578c2ecf20Sopenharmony_ci NDIS_80211_MEDIA_STREAM_ON 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cienum ndis_80211_radio_status { 1618c2ecf20Sopenharmony_ci NDIS_80211_RADIO_STATUS_ON, 1628c2ecf20Sopenharmony_ci NDIS_80211_RADIO_STATUS_HARDWARE_OFF, 1638c2ecf20Sopenharmony_ci NDIS_80211_RADIO_STATUS_SOFTWARE_OFF, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cienum ndis_80211_addkey_bits { 1678c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), 1688c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), 1698c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30), 1708c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31) 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cienum ndis_80211_addwep_bits { 1748c2ecf20Sopenharmony_ci NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30), 1758c2ecf20Sopenharmony_ci NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cienum ndis_80211_power_mode { 1798c2ecf20Sopenharmony_ci NDIS_80211_POWER_MODE_CAM, 1808c2ecf20Sopenharmony_ci NDIS_80211_POWER_MODE_MAX_PSP, 1818c2ecf20Sopenharmony_ci NDIS_80211_POWER_MODE_FAST_PSP, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cienum ndis_80211_pmkid_cand_list_flag_bits { 1858c2ecf20Sopenharmony_ci NDIS_80211_PMKID_CAND_PREAUTH = cpu_to_le32(1 << 0) 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistruct ndis_80211_auth_request { 1898c2ecf20Sopenharmony_ci __le32 length; 1908c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 1918c2ecf20Sopenharmony_ci u8 padding[2]; 1928c2ecf20Sopenharmony_ci __le32 flags; 1938c2ecf20Sopenharmony_ci} __packed; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistruct ndis_80211_pmkid_candidate { 1968c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 1978c2ecf20Sopenharmony_ci u8 padding[2]; 1988c2ecf20Sopenharmony_ci __le32 flags; 1998c2ecf20Sopenharmony_ci} __packed; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistruct ndis_80211_pmkid_cand_list { 2028c2ecf20Sopenharmony_ci __le32 version; 2038c2ecf20Sopenharmony_ci __le32 num_candidates; 2048c2ecf20Sopenharmony_ci struct ndis_80211_pmkid_candidate candidate_list[]; 2058c2ecf20Sopenharmony_ci} __packed; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistruct ndis_80211_status_indication { 2088c2ecf20Sopenharmony_ci __le32 status_type; 2098c2ecf20Sopenharmony_ci union { 2108c2ecf20Sopenharmony_ci __le32 media_stream_mode; 2118c2ecf20Sopenharmony_ci __le32 radio_status; 2128c2ecf20Sopenharmony_ci struct ndis_80211_auth_request auth_request[0]; 2138c2ecf20Sopenharmony_ci struct ndis_80211_pmkid_cand_list cand_list; 2148c2ecf20Sopenharmony_ci } u; 2158c2ecf20Sopenharmony_ci} __packed; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistruct ndis_80211_ssid { 2188c2ecf20Sopenharmony_ci __le32 length; 2198c2ecf20Sopenharmony_ci u8 essid[NDIS_802_11_LENGTH_SSID]; 2208c2ecf20Sopenharmony_ci} __packed; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistruct ndis_80211_conf_freq_hop { 2238c2ecf20Sopenharmony_ci __le32 length; 2248c2ecf20Sopenharmony_ci __le32 hop_pattern; 2258c2ecf20Sopenharmony_ci __le32 hop_set; 2268c2ecf20Sopenharmony_ci __le32 dwell_time; 2278c2ecf20Sopenharmony_ci} __packed; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistruct ndis_80211_conf { 2308c2ecf20Sopenharmony_ci __le32 length; 2318c2ecf20Sopenharmony_ci __le32 beacon_period; 2328c2ecf20Sopenharmony_ci __le32 atim_window; 2338c2ecf20Sopenharmony_ci __le32 ds_config; 2348c2ecf20Sopenharmony_ci struct ndis_80211_conf_freq_hop fh_config; 2358c2ecf20Sopenharmony_ci} __packed; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistruct ndis_80211_bssid_ex { 2388c2ecf20Sopenharmony_ci __le32 length; 2398c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 2408c2ecf20Sopenharmony_ci u8 padding[2]; 2418c2ecf20Sopenharmony_ci struct ndis_80211_ssid ssid; 2428c2ecf20Sopenharmony_ci __le32 privacy; 2438c2ecf20Sopenharmony_ci __le32 rssi; 2448c2ecf20Sopenharmony_ci __le32 net_type; 2458c2ecf20Sopenharmony_ci struct ndis_80211_conf config; 2468c2ecf20Sopenharmony_ci __le32 net_infra; 2478c2ecf20Sopenharmony_ci u8 rates[NDIS_802_11_LENGTH_RATES_EX]; 2488c2ecf20Sopenharmony_ci __le32 ie_length; 2498c2ecf20Sopenharmony_ci u8 ies[]; 2508c2ecf20Sopenharmony_ci} __packed; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistruct ndis_80211_bssid_list_ex { 2538c2ecf20Sopenharmony_ci __le32 num_items; 2548c2ecf20Sopenharmony_ci struct ndis_80211_bssid_ex bssid[]; 2558c2ecf20Sopenharmony_ci} __packed; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistruct ndis_80211_fixed_ies { 2588c2ecf20Sopenharmony_ci u8 timestamp[8]; 2598c2ecf20Sopenharmony_ci __le16 beacon_interval; 2608c2ecf20Sopenharmony_ci __le16 capabilities; 2618c2ecf20Sopenharmony_ci} __packed; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistruct ndis_80211_wep_key { 2648c2ecf20Sopenharmony_ci __le32 size; 2658c2ecf20Sopenharmony_ci __le32 index; 2668c2ecf20Sopenharmony_ci __le32 length; 2678c2ecf20Sopenharmony_ci u8 material[32]; 2688c2ecf20Sopenharmony_ci} __packed; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistruct ndis_80211_key { 2718c2ecf20Sopenharmony_ci __le32 size; 2728c2ecf20Sopenharmony_ci __le32 index; 2738c2ecf20Sopenharmony_ci __le32 length; 2748c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 2758c2ecf20Sopenharmony_ci u8 padding[6]; 2768c2ecf20Sopenharmony_ci u8 rsc[8]; 2778c2ecf20Sopenharmony_ci u8 material[32]; 2788c2ecf20Sopenharmony_ci} __packed; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistruct ndis_80211_remove_key { 2818c2ecf20Sopenharmony_ci __le32 size; 2828c2ecf20Sopenharmony_ci __le32 index; 2838c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 2848c2ecf20Sopenharmony_ci u8 padding[2]; 2858c2ecf20Sopenharmony_ci} __packed; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistruct ndis_config_param { 2888c2ecf20Sopenharmony_ci __le32 name_offs; 2898c2ecf20Sopenharmony_ci __le32 name_length; 2908c2ecf20Sopenharmony_ci __le32 type; 2918c2ecf20Sopenharmony_ci __le32 value_offs; 2928c2ecf20Sopenharmony_ci __le32 value_length; 2938c2ecf20Sopenharmony_ci} __packed; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistruct ndis_80211_assoc_info { 2968c2ecf20Sopenharmony_ci __le32 length; 2978c2ecf20Sopenharmony_ci __le16 req_ies; 2988c2ecf20Sopenharmony_ci struct req_ie { 2998c2ecf20Sopenharmony_ci __le16 capa; 3008c2ecf20Sopenharmony_ci __le16 listen_interval; 3018c2ecf20Sopenharmony_ci u8 cur_ap_address[ETH_ALEN]; 3028c2ecf20Sopenharmony_ci } req_ie; 3038c2ecf20Sopenharmony_ci __le32 req_ie_length; 3048c2ecf20Sopenharmony_ci __le32 offset_req_ies; 3058c2ecf20Sopenharmony_ci __le16 resp_ies; 3068c2ecf20Sopenharmony_ci struct resp_ie { 3078c2ecf20Sopenharmony_ci __le16 capa; 3088c2ecf20Sopenharmony_ci __le16 status_code; 3098c2ecf20Sopenharmony_ci __le16 assoc_id; 3108c2ecf20Sopenharmony_ci } resp_ie; 3118c2ecf20Sopenharmony_ci __le32 resp_ie_length; 3128c2ecf20Sopenharmony_ci __le32 offset_resp_ies; 3138c2ecf20Sopenharmony_ci} __packed; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistruct ndis_80211_capability { 3168c2ecf20Sopenharmony_ci __le32 length; 3178c2ecf20Sopenharmony_ci __le32 version; 3188c2ecf20Sopenharmony_ci __le32 num_pmkids; 3198c2ecf20Sopenharmony_ci __le32 num_auth_encr_pair; 3208c2ecf20Sopenharmony_ci} __packed; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistruct ndis_80211_bssid_info { 3238c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 3248c2ecf20Sopenharmony_ci u8 pmkid[16]; 3258c2ecf20Sopenharmony_ci} __packed; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistruct ndis_80211_pmkid { 3288c2ecf20Sopenharmony_ci __le32 length; 3298c2ecf20Sopenharmony_ci __le32 bssid_info_count; 3308c2ecf20Sopenharmony_ci struct ndis_80211_bssid_info bssid_info[]; 3318c2ecf20Sopenharmony_ci} __packed; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * private data 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci#define CAP_MODE_80211A 1 3378c2ecf20Sopenharmony_ci#define CAP_MODE_80211B 2 3388c2ecf20Sopenharmony_ci#define CAP_MODE_80211G 4 3398c2ecf20Sopenharmony_ci#define CAP_MODE_MASK 7 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci#define WORK_LINK_UP 0 3428c2ecf20Sopenharmony_ci#define WORK_LINK_DOWN 1 3438c2ecf20Sopenharmony_ci#define WORK_SET_MULTICAST_LIST 2 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci#define RNDIS_WLAN_ALG_NONE 0 3468c2ecf20Sopenharmony_ci#define RNDIS_WLAN_ALG_WEP (1<<0) 3478c2ecf20Sopenharmony_ci#define RNDIS_WLAN_ALG_TKIP (1<<1) 3488c2ecf20Sopenharmony_ci#define RNDIS_WLAN_ALG_CCMP (1<<2) 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#define RNDIS_WLAN_NUM_KEYS 4 3518c2ecf20Sopenharmony_ci#define RNDIS_WLAN_KEY_MGMT_NONE 0 3528c2ecf20Sopenharmony_ci#define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) 3538c2ecf20Sopenharmony_ci#define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic const struct ieee80211_channel rndis_channels[] = { 3588c2ecf20Sopenharmony_ci { .center_freq = 2412 }, 3598c2ecf20Sopenharmony_ci { .center_freq = 2417 }, 3608c2ecf20Sopenharmony_ci { .center_freq = 2422 }, 3618c2ecf20Sopenharmony_ci { .center_freq = 2427 }, 3628c2ecf20Sopenharmony_ci { .center_freq = 2432 }, 3638c2ecf20Sopenharmony_ci { .center_freq = 2437 }, 3648c2ecf20Sopenharmony_ci { .center_freq = 2442 }, 3658c2ecf20Sopenharmony_ci { .center_freq = 2447 }, 3668c2ecf20Sopenharmony_ci { .center_freq = 2452 }, 3678c2ecf20Sopenharmony_ci { .center_freq = 2457 }, 3688c2ecf20Sopenharmony_ci { .center_freq = 2462 }, 3698c2ecf20Sopenharmony_ci { .center_freq = 2467 }, 3708c2ecf20Sopenharmony_ci { .center_freq = 2472 }, 3718c2ecf20Sopenharmony_ci { .center_freq = 2484 }, 3728c2ecf20Sopenharmony_ci}; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic const struct ieee80211_rate rndis_rates[] = { 3758c2ecf20Sopenharmony_ci { .bitrate = 10 }, 3768c2ecf20Sopenharmony_ci { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3778c2ecf20Sopenharmony_ci { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3788c2ecf20Sopenharmony_ci { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 3798c2ecf20Sopenharmony_ci { .bitrate = 60 }, 3808c2ecf20Sopenharmony_ci { .bitrate = 90 }, 3818c2ecf20Sopenharmony_ci { .bitrate = 120 }, 3828c2ecf20Sopenharmony_ci { .bitrate = 180 }, 3838c2ecf20Sopenharmony_ci { .bitrate = 240 }, 3848c2ecf20Sopenharmony_ci { .bitrate = 360 }, 3858c2ecf20Sopenharmony_ci { .bitrate = 480 }, 3868c2ecf20Sopenharmony_ci { .bitrate = 540 } 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic const u32 rndis_cipher_suites[] = { 3908c2ecf20Sopenharmony_ci WLAN_CIPHER_SUITE_WEP40, 3918c2ecf20Sopenharmony_ci WLAN_CIPHER_SUITE_WEP104, 3928c2ecf20Sopenharmony_ci WLAN_CIPHER_SUITE_TKIP, 3938c2ecf20Sopenharmony_ci WLAN_CIPHER_SUITE_CCMP, 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistruct rndis_wlan_encr_key { 3978c2ecf20Sopenharmony_ci int len; 3988c2ecf20Sopenharmony_ci u32 cipher; 3998c2ecf20Sopenharmony_ci u8 material[32]; 4008c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 4018c2ecf20Sopenharmony_ci bool pairwise; 4028c2ecf20Sopenharmony_ci bool tx_key; 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* RNDIS device private data */ 4068c2ecf20Sopenharmony_cistruct rndis_wlan_private { 4078c2ecf20Sopenharmony_ci struct usbnet *usbdev; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci struct wireless_dev wdev; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci struct cfg80211_scan_request *scan_request; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci struct workqueue_struct *workqueue; 4148c2ecf20Sopenharmony_ci struct delayed_work dev_poller_work; 4158c2ecf20Sopenharmony_ci struct delayed_work scan_work; 4168c2ecf20Sopenharmony_ci struct work_struct work; 4178c2ecf20Sopenharmony_ci struct mutex command_lock; 4188c2ecf20Sopenharmony_ci unsigned long work_pending; 4198c2ecf20Sopenharmony_ci int last_qual; 4208c2ecf20Sopenharmony_ci s32 cqm_rssi_thold; 4218c2ecf20Sopenharmony_ci u32 cqm_rssi_hyst; 4228c2ecf20Sopenharmony_ci int last_cqm_event_rssi; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci struct ieee80211_supported_band band; 4258c2ecf20Sopenharmony_ci struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; 4268c2ecf20Sopenharmony_ci struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; 4278c2ecf20Sopenharmony_ci u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)]; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci int device_type; 4308c2ecf20Sopenharmony_ci int caps; 4318c2ecf20Sopenharmony_ci int multicast_size; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* module parameters */ 4348c2ecf20Sopenharmony_ci char param_country[4]; 4358c2ecf20Sopenharmony_ci int param_frameburst; 4368c2ecf20Sopenharmony_ci int param_afterburner; 4378c2ecf20Sopenharmony_ci int param_power_save; 4388c2ecf20Sopenharmony_ci int param_power_output; 4398c2ecf20Sopenharmony_ci int param_roamtrigger; 4408c2ecf20Sopenharmony_ci int param_roamdelta; 4418c2ecf20Sopenharmony_ci u32 param_workaround_interval; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* hardware state */ 4448c2ecf20Sopenharmony_ci bool radio_on; 4458c2ecf20Sopenharmony_ci int power_mode; 4468c2ecf20Sopenharmony_ci int infra_mode; 4478c2ecf20Sopenharmony_ci bool connected; 4488c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 4498c2ecf20Sopenharmony_ci u32 current_command_oid; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* encryption stuff */ 4528c2ecf20Sopenharmony_ci u8 encr_tx_key_index; 4538c2ecf20Sopenharmony_ci struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS]; 4548c2ecf20Sopenharmony_ci int wpa_version; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci u8 command_buffer[COMMAND_BUFFER_SIZE]; 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci * cfg80211 ops 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_cistatic int rndis_change_virtual_intf(struct wiphy *wiphy, 4638c2ecf20Sopenharmony_ci struct net_device *dev, 4648c2ecf20Sopenharmony_ci enum nl80211_iftype type, 4658c2ecf20Sopenharmony_ci struct vif_params *params); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int rndis_scan(struct wiphy *wiphy, 4688c2ecf20Sopenharmony_ci struct cfg80211_scan_request *request); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int rndis_set_tx_power(struct wiphy *wiphy, 4738c2ecf20Sopenharmony_ci struct wireless_dev *wdev, 4748c2ecf20Sopenharmony_ci enum nl80211_tx_power_setting type, 4758c2ecf20Sopenharmony_ci int mbm); 4768c2ecf20Sopenharmony_cistatic int rndis_get_tx_power(struct wiphy *wiphy, 4778c2ecf20Sopenharmony_ci struct wireless_dev *wdev, 4788c2ecf20Sopenharmony_ci int *dbm); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int rndis_connect(struct wiphy *wiphy, struct net_device *dev, 4818c2ecf20Sopenharmony_ci struct cfg80211_connect_params *sme); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, 4848c2ecf20Sopenharmony_ci u16 reason_code); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, 4878c2ecf20Sopenharmony_ci struct cfg80211_ibss_params *params); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, 4928c2ecf20Sopenharmony_ci u8 key_index, bool pairwise, const u8 *mac_addr, 4938c2ecf20Sopenharmony_ci struct key_params *params); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, 4968c2ecf20Sopenharmony_ci u8 key_index, bool pairwise, const u8 *mac_addr); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, 4998c2ecf20Sopenharmony_ci u8 key_index, bool unicast, bool multicast); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, 5028c2ecf20Sopenharmony_ci const u8 *mac, struct station_info *sinfo); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, 5058c2ecf20Sopenharmony_ci int idx, u8 *mac, struct station_info *sinfo); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, 5088c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, 5118c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 5168c2ecf20Sopenharmony_ci bool enabled, int timeout); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int rndis_set_cqm_rssi_config(struct wiphy *wiphy, 5198c2ecf20Sopenharmony_ci struct net_device *dev, 5208c2ecf20Sopenharmony_ci s32 rssi_thold, u32 rssi_hyst); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct cfg80211_ops rndis_config_ops = { 5238c2ecf20Sopenharmony_ci .change_virtual_intf = rndis_change_virtual_intf, 5248c2ecf20Sopenharmony_ci .scan = rndis_scan, 5258c2ecf20Sopenharmony_ci .set_wiphy_params = rndis_set_wiphy_params, 5268c2ecf20Sopenharmony_ci .set_tx_power = rndis_set_tx_power, 5278c2ecf20Sopenharmony_ci .get_tx_power = rndis_get_tx_power, 5288c2ecf20Sopenharmony_ci .connect = rndis_connect, 5298c2ecf20Sopenharmony_ci .disconnect = rndis_disconnect, 5308c2ecf20Sopenharmony_ci .join_ibss = rndis_join_ibss, 5318c2ecf20Sopenharmony_ci .leave_ibss = rndis_leave_ibss, 5328c2ecf20Sopenharmony_ci .add_key = rndis_add_key, 5338c2ecf20Sopenharmony_ci .del_key = rndis_del_key, 5348c2ecf20Sopenharmony_ci .set_default_key = rndis_set_default_key, 5358c2ecf20Sopenharmony_ci .get_station = rndis_get_station, 5368c2ecf20Sopenharmony_ci .dump_station = rndis_dump_station, 5378c2ecf20Sopenharmony_ci .set_pmksa = rndis_set_pmksa, 5388c2ecf20Sopenharmony_ci .del_pmksa = rndis_del_pmksa, 5398c2ecf20Sopenharmony_ci .flush_pmksa = rndis_flush_pmksa, 5408c2ecf20Sopenharmony_ci .set_power_mgmt = rndis_set_power_mgmt, 5418c2ecf20Sopenharmony_ci .set_cqm_rssi_config = rndis_set_cqm_rssi_config, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void *rndis_wiphy_privid = &rndis_wiphy_privid; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci return (struct rndis_wlan_private *)dev->driver_priv; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci switch (priv->param_power_output) { 5558c2ecf20Sopenharmony_ci default: 5568c2ecf20Sopenharmony_ci case 3: 5578c2ecf20Sopenharmony_ci return BCM4320_DEFAULT_TXPOWER_DBM_100; 5588c2ecf20Sopenharmony_ci case 2: 5598c2ecf20Sopenharmony_ci return BCM4320_DEFAULT_TXPOWER_DBM_75; 5608c2ecf20Sopenharmony_ci case 1: 5618c2ecf20Sopenharmony_ci return BCM4320_DEFAULT_TXPOWER_DBM_50; 5628c2ecf20Sopenharmony_ci case 0: 5638c2ecf20Sopenharmony_ci return BCM4320_DEFAULT_TXPOWER_DBM_25; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic bool is_wpa_key(struct rndis_wlan_private *priv, u8 idx) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int cipher = priv->encr_keys[idx].cipher; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return (cipher == WLAN_CIPHER_SUITE_CCMP || 5728c2ecf20Sopenharmony_ci cipher == WLAN_CIPHER_SUITE_TKIP); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int rndis_cipher_to_alg(u32 cipher) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci switch (cipher) { 5788c2ecf20Sopenharmony_ci default: 5798c2ecf20Sopenharmony_ci return RNDIS_WLAN_ALG_NONE; 5808c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 5818c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 5828c2ecf20Sopenharmony_ci return RNDIS_WLAN_ALG_WEP; 5838c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 5848c2ecf20Sopenharmony_ci return RNDIS_WLAN_ALG_TKIP; 5858c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 5868c2ecf20Sopenharmony_ci return RNDIS_WLAN_ALG_CCMP; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int rndis_akm_suite_to_key_mgmt(u32 akm_suite) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci switch (akm_suite) { 5938c2ecf20Sopenharmony_ci default: 5948c2ecf20Sopenharmony_ci return RNDIS_WLAN_KEY_MGMT_NONE; 5958c2ecf20Sopenharmony_ci case WLAN_AKM_SUITE_8021X: 5968c2ecf20Sopenharmony_ci return RNDIS_WLAN_KEY_MGMT_802_1X; 5978c2ecf20Sopenharmony_ci case WLAN_AKM_SUITE_PSK: 5988c2ecf20Sopenharmony_ci return RNDIS_WLAN_KEY_MGMT_PSK; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci#ifdef DEBUG 6038c2ecf20Sopenharmony_cistatic const char *oid_to_string(u32 oid) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci switch (oid) { 6068c2ecf20Sopenharmony_ci#define OID_STR(oid) case oid: return(#oid) 6078c2ecf20Sopenharmony_ci /* from rndis_host.h */ 6088c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_3_PERMANENT_ADDRESS); 6098c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE); 6108c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); 6118c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_PHYSICAL_MEDIUM); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* from rndis_wlan.c */ 6148c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_LINK_SPEED); 6158c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_XMIT_OK); 6188c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_RCV_OK); 6198c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_XMIT_ERROR); 6208c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_RCV_ERROR); 6218c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_GEN_RCV_NO_BUFFER); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_3_CURRENT_ADDRESS); 6248c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_3_MULTICAST_LIST); 6258c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_3_MAXIMUM_LIST_SIZE); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_BSSID); 6288c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_SSID); 6298c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_INFRASTRUCTURE_MODE); 6308c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_ADD_WEP); 6318c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_REMOVE_WEP); 6328c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_DISASSOCIATE); 6338c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_AUTHENTICATION_MODE); 6348c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_PRIVACY_FILTER); 6358c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_BSSID_LIST_SCAN); 6368c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_ENCRYPTION_STATUS); 6378c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_ADD_KEY); 6388c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_REMOVE_KEY); 6398c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_ASSOCIATION_INFORMATION); 6408c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_CAPABILITY); 6418c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_PMKID); 6428c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED); 6438c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_NETWORK_TYPE_IN_USE); 6448c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_TX_POWER_LEVEL); 6458c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_RSSI); 6468c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_RSSI_TRIGGER); 6478c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD); 6488c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_RTS_THRESHOLD); 6498c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_SUPPORTED_RATES); 6508c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_CONFIGURATION); 6518c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_POWER_MODE); 6528c2ecf20Sopenharmony_ci OID_STR(RNDIS_OID_802_11_BSSID_LIST); 6538c2ecf20Sopenharmony_ci#undef OID_STR 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return "?"; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci#else 6598c2ecf20Sopenharmony_cistatic const char *oid_to_string(u32 oid) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci return "?"; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci#endif 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* translate error code */ 6668c2ecf20Sopenharmony_cistatic int rndis_error_status(__le32 rndis_status) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci int ret = -EINVAL; 6698c2ecf20Sopenharmony_ci switch (le32_to_cpu(rndis_status)) { 6708c2ecf20Sopenharmony_ci case RNDIS_STATUS_SUCCESS: 6718c2ecf20Sopenharmony_ci ret = 0; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci case RNDIS_STATUS_FAILURE: 6748c2ecf20Sopenharmony_ci case RNDIS_STATUS_INVALID_DATA: 6758c2ecf20Sopenharmony_ci ret = -EINVAL; 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci case RNDIS_STATUS_NOT_SUPPORTED: 6788c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci case RNDIS_STATUS_ADAPTER_NOT_READY: 6818c2ecf20Sopenharmony_ci case RNDIS_STATUS_ADAPTER_NOT_OPEN: 6828c2ecf20Sopenharmony_ci ret = -EBUSY; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); 6918c2ecf20Sopenharmony_ci union { 6928c2ecf20Sopenharmony_ci void *buf; 6938c2ecf20Sopenharmony_ci struct rndis_msg_hdr *header; 6948c2ecf20Sopenharmony_ci struct rndis_query *get; 6958c2ecf20Sopenharmony_ci struct rndis_query_c *get_c; 6968c2ecf20Sopenharmony_ci } u; 6978c2ecf20Sopenharmony_ci int ret; 6988c2ecf20Sopenharmony_ci size_t buflen, resplen, respoffs, copylen; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci buflen = *len + sizeof(*u.get); 7018c2ecf20Sopenharmony_ci if (buflen < CONTROL_BUFFER_SIZE) 7028c2ecf20Sopenharmony_ci buflen = CONTROL_BUFFER_SIZE; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (buflen > COMMAND_BUFFER_SIZE) { 7058c2ecf20Sopenharmony_ci u.buf = kmalloc(buflen, GFP_KERNEL); 7068c2ecf20Sopenharmony_ci if (!u.buf) 7078c2ecf20Sopenharmony_ci return -ENOMEM; 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci u.buf = priv->command_buffer; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci mutex_lock(&priv->command_lock); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci memset(u.get, 0, sizeof *u.get); 7158c2ecf20Sopenharmony_ci u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); 7168c2ecf20Sopenharmony_ci u.get->msg_len = cpu_to_le32(sizeof *u.get); 7178c2ecf20Sopenharmony_ci u.get->oid = cpu_to_le32(oid); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci priv->current_command_oid = oid; 7208c2ecf20Sopenharmony_ci ret = rndis_command(dev, u.header, buflen); 7218c2ecf20Sopenharmony_ci priv->current_command_oid = 0; 7228c2ecf20Sopenharmony_ci if (ret < 0) 7238c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", 7248c2ecf20Sopenharmony_ci __func__, oid_to_string(oid), ret, 7258c2ecf20Sopenharmony_ci le32_to_cpu(u.get_c->status)); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (ret == 0) { 7288c2ecf20Sopenharmony_ci resplen = le32_to_cpu(u.get_c->len); 7298c2ecf20Sopenharmony_ci respoffs = le32_to_cpu(u.get_c->offset) + 8; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (respoffs > buflen) { 7328c2ecf20Sopenharmony_ci /* Device returned data offset outside buffer, error. */ 7338c2ecf20Sopenharmony_ci netdev_dbg(dev->net, 7348c2ecf20Sopenharmony_ci "%s(%s): received invalid data offset: %zu > %zu\n", 7358c2ecf20Sopenharmony_ci __func__, oid_to_string(oid), respoffs, buflen); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci ret = -EINVAL; 7388c2ecf20Sopenharmony_ci goto exit_unlock; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci copylen = min(resplen, buflen - respoffs); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (copylen > *len) 7448c2ecf20Sopenharmony_ci copylen = *len; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci memcpy(data, u.buf + respoffs, copylen); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci *len = resplen; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci ret = rndis_error_status(u.get_c->status); 7518c2ecf20Sopenharmony_ci if (ret < 0) 7528c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", 7538c2ecf20Sopenharmony_ci __func__, oid_to_string(oid), 7548c2ecf20Sopenharmony_ci le32_to_cpu(u.get_c->status), ret); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ciexit_unlock: 7588c2ecf20Sopenharmony_ci mutex_unlock(&priv->command_lock); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (u.buf != priv->command_buffer) 7618c2ecf20Sopenharmony_ci kfree(u.buf); 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int rndis_set_oid(struct usbnet *dev, u32 oid, const void *data, 7668c2ecf20Sopenharmony_ci int len) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); 7698c2ecf20Sopenharmony_ci union { 7708c2ecf20Sopenharmony_ci void *buf; 7718c2ecf20Sopenharmony_ci struct rndis_msg_hdr *header; 7728c2ecf20Sopenharmony_ci struct rndis_set *set; 7738c2ecf20Sopenharmony_ci struct rndis_set_c *set_c; 7748c2ecf20Sopenharmony_ci } u; 7758c2ecf20Sopenharmony_ci int ret, buflen; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci buflen = len + sizeof(*u.set); 7788c2ecf20Sopenharmony_ci if (buflen < CONTROL_BUFFER_SIZE) 7798c2ecf20Sopenharmony_ci buflen = CONTROL_BUFFER_SIZE; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (buflen > COMMAND_BUFFER_SIZE) { 7828c2ecf20Sopenharmony_ci u.buf = kmalloc(buflen, GFP_KERNEL); 7838c2ecf20Sopenharmony_ci if (!u.buf) 7848c2ecf20Sopenharmony_ci return -ENOMEM; 7858c2ecf20Sopenharmony_ci } else { 7868c2ecf20Sopenharmony_ci u.buf = priv->command_buffer; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci mutex_lock(&priv->command_lock); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci memset(u.set, 0, sizeof *u.set); 7928c2ecf20Sopenharmony_ci u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); 7938c2ecf20Sopenharmony_ci u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len); 7948c2ecf20Sopenharmony_ci u.set->oid = cpu_to_le32(oid); 7958c2ecf20Sopenharmony_ci u.set->len = cpu_to_le32(len); 7968c2ecf20Sopenharmony_ci u.set->offset = cpu_to_le32(sizeof(*u.set) - 8); 7978c2ecf20Sopenharmony_ci u.set->handle = cpu_to_le32(0); 7988c2ecf20Sopenharmony_ci memcpy(u.buf + sizeof(*u.set), data, len); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci priv->current_command_oid = oid; 8018c2ecf20Sopenharmony_ci ret = rndis_command(dev, u.header, buflen); 8028c2ecf20Sopenharmony_ci priv->current_command_oid = 0; 8038c2ecf20Sopenharmony_ci if (ret < 0) 8048c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", 8058c2ecf20Sopenharmony_ci __func__, oid_to_string(oid), ret, 8068c2ecf20Sopenharmony_ci le32_to_cpu(u.set_c->status)); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (ret == 0) { 8098c2ecf20Sopenharmony_ci ret = rndis_error_status(u.set_c->status); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (ret < 0) 8128c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", 8138c2ecf20Sopenharmony_ci __func__, oid_to_string(oid), 8148c2ecf20Sopenharmony_ci le32_to_cpu(u.set_c->status), ret); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mutex_unlock(&priv->command_lock); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (u.buf != priv->command_buffer) 8208c2ecf20Sopenharmony_ci kfree(u.buf); 8218c2ecf20Sopenharmony_ci return ret; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int rndis_reset(struct usbnet *usbdev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 8278c2ecf20Sopenharmony_ci struct rndis_reset *reset; 8288c2ecf20Sopenharmony_ci int ret; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mutex_lock(&priv->command_lock); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci reset = (void *)priv->command_buffer; 8338c2ecf20Sopenharmony_ci memset(reset, 0, sizeof(*reset)); 8348c2ecf20Sopenharmony_ci reset->msg_type = cpu_to_le32(RNDIS_MSG_RESET); 8358c2ecf20Sopenharmony_ci reset->msg_len = cpu_to_le32(sizeof(*reset)); 8368c2ecf20Sopenharmony_ci priv->current_command_oid = 0; 8378c2ecf20Sopenharmony_ci ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci mutex_unlock(&priv->command_lock); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (ret < 0) 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci return 0; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* 8478c2ecf20Sopenharmony_ci * Specs say that we can only set config parameters only soon after device 8488c2ecf20Sopenharmony_ci * initialization. 8498c2ecf20Sopenharmony_ci * value_type: 0 = u32, 2 = unicode string 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_cistatic int rndis_set_config_parameter(struct usbnet *dev, char *param, 8528c2ecf20Sopenharmony_ci int value_type, void *value) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct ndis_config_param *infobuf; 8558c2ecf20Sopenharmony_ci int value_len, info_len, param_len, ret, i; 8568c2ecf20Sopenharmony_ci __le16 *unibuf; 8578c2ecf20Sopenharmony_ci __le32 *dst_value; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (value_type == 0) 8608c2ecf20Sopenharmony_ci value_len = sizeof(__le32); 8618c2ecf20Sopenharmony_ci else if (value_type == 2) 8628c2ecf20Sopenharmony_ci value_len = strlen(value) * sizeof(__le16); 8638c2ecf20Sopenharmony_ci else 8648c2ecf20Sopenharmony_ci return -EINVAL; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci param_len = strlen(param) * sizeof(__le16); 8678c2ecf20Sopenharmony_ci info_len = sizeof(*infobuf) + param_len + value_len; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci#ifdef DEBUG 8708c2ecf20Sopenharmony_ci info_len += 12; 8718c2ecf20Sopenharmony_ci#endif 8728c2ecf20Sopenharmony_ci infobuf = kmalloc(info_len, GFP_KERNEL); 8738c2ecf20Sopenharmony_ci if (!infobuf) 8748c2ecf20Sopenharmony_ci return -ENOMEM; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci#ifdef DEBUG 8778c2ecf20Sopenharmony_ci info_len -= 12; 8788c2ecf20Sopenharmony_ci /* extra 12 bytes are for padding (debug output) */ 8798c2ecf20Sopenharmony_ci memset(infobuf, 0xCC, info_len + 12); 8808c2ecf20Sopenharmony_ci#endif 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (value_type == 2) 8838c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "setting config parameter: %s, value: %s\n", 8848c2ecf20Sopenharmony_ci param, (u8 *)value); 8858c2ecf20Sopenharmony_ci else 8868c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "setting config parameter: %s, value: %d\n", 8878c2ecf20Sopenharmony_ci param, *(u32 *)value); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci infobuf->name_offs = cpu_to_le32(sizeof(*infobuf)); 8908c2ecf20Sopenharmony_ci infobuf->name_length = cpu_to_le32(param_len); 8918c2ecf20Sopenharmony_ci infobuf->type = cpu_to_le32(value_type); 8928c2ecf20Sopenharmony_ci infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len); 8938c2ecf20Sopenharmony_ci infobuf->value_length = cpu_to_le32(value_len); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* simple string to unicode string conversion */ 8968c2ecf20Sopenharmony_ci unibuf = (void *)infobuf + sizeof(*infobuf); 8978c2ecf20Sopenharmony_ci for (i = 0; i < param_len / sizeof(__le16); i++) 8988c2ecf20Sopenharmony_ci unibuf[i] = cpu_to_le16(param[i]); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (value_type == 2) { 9018c2ecf20Sopenharmony_ci unibuf = (void *)infobuf + sizeof(*infobuf) + param_len; 9028c2ecf20Sopenharmony_ci for (i = 0; i < value_len / sizeof(__le16); i++) 9038c2ecf20Sopenharmony_ci unibuf[i] = cpu_to_le16(((u8 *)value)[i]); 9048c2ecf20Sopenharmony_ci } else { 9058c2ecf20Sopenharmony_ci dst_value = (void *)infobuf + sizeof(*infobuf) + param_len; 9068c2ecf20Sopenharmony_ci *dst_value = cpu_to_le32(*(u32 *)value); 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci#ifdef DEBUG 9108c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "info buffer (len: %d)\n", info_len); 9118c2ecf20Sopenharmony_ci for (i = 0; i < info_len; i += 12) { 9128c2ecf20Sopenharmony_ci u32 *tmp = (u32 *)((u8 *)infobuf + i); 9138c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "%08X:%08X:%08X\n", 9148c2ecf20Sopenharmony_ci cpu_to_be32(tmp[0]), 9158c2ecf20Sopenharmony_ci cpu_to_be32(tmp[1]), 9168c2ecf20Sopenharmony_ci cpu_to_be32(tmp[2])); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci#endif 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ret = rndis_set_oid(dev, RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER, 9218c2ecf20Sopenharmony_ci infobuf, info_len); 9228c2ecf20Sopenharmony_ci if (ret != 0) 9238c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "setting rndis config parameter failed, %d\n", 9248c2ecf20Sopenharmony_ci ret); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci kfree(infobuf); 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int rndis_set_config_parameter_str(struct usbnet *dev, 9318c2ecf20Sopenharmony_ci char *param, char *value) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci return rndis_set_config_parameter(dev, param, 2, value); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/* 9378c2ecf20Sopenharmony_ci * data conversion functions 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_cistatic int level_to_qual(int level) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE); 9428c2ecf20Sopenharmony_ci return qual >= 0 ? (qual <= 100 ? qual : 100) : 0; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci/* 9468c2ecf20Sopenharmony_ci * common functions 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_cistatic int set_infra_mode(struct usbnet *usbdev, int mode); 9498c2ecf20Sopenharmony_cistatic void restore_keys(struct usbnet *usbdev); 9508c2ecf20Sopenharmony_cistatic int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, 9518c2ecf20Sopenharmony_ci bool *matched); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic int rndis_start_bssid_list_scan(struct usbnet *usbdev) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci __le32 tmp; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* Note: RNDIS_OID_802_11_BSSID_LIST_SCAN clears internal BSS list. */ 9588c2ecf20Sopenharmony_ci tmp = cpu_to_le32(1); 9598c2ecf20Sopenharmony_ci return rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST_SCAN, &tmp, 9608c2ecf20Sopenharmony_ci sizeof(tmp)); 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 9668c2ecf20Sopenharmony_ci int ret; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_SSID, 9698c2ecf20Sopenharmony_ci ssid, sizeof(*ssid)); 9708c2ecf20Sopenharmony_ci if (ret < 0) { 9718c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "setting SSID failed (%08X)\n", ret); 9728c2ecf20Sopenharmony_ci return ret; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci if (ret == 0) { 9758c2ecf20Sopenharmony_ci priv->radio_on = true; 9768c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci return ret; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int set_bssid(struct usbnet *usbdev, const u8 *bssid) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci int ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID, 9878c2ecf20Sopenharmony_ci bssid, ETH_ALEN); 9888c2ecf20Sopenharmony_ci if (ret < 0) { 9898c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "setting BSSID[%pM] failed (%08X)\n", 9908c2ecf20Sopenharmony_ci bssid, ret); 9918c2ecf20Sopenharmony_ci return ret; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return ret; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int clear_bssid(struct usbnet *usbdev) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci static const u8 broadcast_mac[ETH_ALEN] = { 10008c2ecf20Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 10018c2ecf20Sopenharmony_ci }; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return set_bssid(usbdev, broadcast_mac); 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci int ret, len; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci len = ETH_ALEN; 10118c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID, 10128c2ecf20Sopenharmony_ci bssid, &len); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (ret != 0) 10158c2ecf20Sopenharmony_ci eth_zero_addr(bssid); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci return ret; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic int get_association_info(struct usbnet *usbdev, 10218c2ecf20Sopenharmony_ci struct ndis_80211_assoc_info *info, int len) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci return rndis_query_oid(usbdev, 10248c2ecf20Sopenharmony_ci RNDIS_OID_802_11_ASSOCIATION_INFORMATION, 10258c2ecf20Sopenharmony_ci info, &len); 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic bool is_associated(struct usbnet *usbdev) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 10318c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 10328c2ecf20Sopenharmony_ci int ret; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (!priv->radio_on) 10358c2ecf20Sopenharmony_ci return false; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci ret = get_bssid(usbdev, bssid); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return (ret == 0 && !is_zero_ether_addr(bssid)); 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic int disassociate(struct usbnet *usbdev, bool reset_ssid) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 10458c2ecf20Sopenharmony_ci struct ndis_80211_ssid ssid; 10468c2ecf20Sopenharmony_ci int i, ret = 0; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (priv->radio_on) { 10498c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 10508c2ecf20Sopenharmony_ci RNDIS_OID_802_11_DISASSOCIATE, 10518c2ecf20Sopenharmony_ci NULL, 0); 10528c2ecf20Sopenharmony_ci if (ret == 0) { 10538c2ecf20Sopenharmony_ci priv->radio_on = false; 10548c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): radio_on = false\n", 10558c2ecf20Sopenharmony_ci __func__); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (reset_ssid) 10588c2ecf20Sopenharmony_ci msleep(100); 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* disassociate causes radio to be turned off; if reset_ssid 10638c2ecf20Sopenharmony_ci * is given, set random ssid to enable radio */ 10648c2ecf20Sopenharmony_ci if (reset_ssid) { 10658c2ecf20Sopenharmony_ci /* Set device to infrastructure mode so we don't get ad-hoc 10668c2ecf20Sopenharmony_ci * 'media connect' indications with the random ssid. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_ci set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci ssid.length = cpu_to_le32(sizeof(ssid.essid)); 10718c2ecf20Sopenharmony_ci get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2); 10728c2ecf20Sopenharmony_ci ssid.essid[0] = 0x1; 10738c2ecf20Sopenharmony_ci ssid.essid[1] = 0xff; 10748c2ecf20Sopenharmony_ci for (i = 2; i < sizeof(ssid.essid); i++) 10758c2ecf20Sopenharmony_ci ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff); 10768c2ecf20Sopenharmony_ci ret = set_essid(usbdev, &ssid); 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci return ret; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, 10828c2ecf20Sopenharmony_ci enum nl80211_auth_type auth_type, int keymgmt) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 10858c2ecf20Sopenharmony_ci __le32 tmp; 10868c2ecf20Sopenharmony_ci int auth_mode, ret; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x authalg=0x%x keymgmt=0x%x\n", 10898c2ecf20Sopenharmony_ci __func__, wpa_version, auth_type, keymgmt); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (wpa_version & NL80211_WPA_VERSION_2) { 10928c2ecf20Sopenharmony_ci if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) 10938c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_WPA2; 10948c2ecf20Sopenharmony_ci else 10958c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_WPA2_PSK; 10968c2ecf20Sopenharmony_ci } else if (wpa_version & NL80211_WPA_VERSION_1) { 10978c2ecf20Sopenharmony_ci if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) 10988c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_WPA; 10998c2ecf20Sopenharmony_ci else if (keymgmt & RNDIS_WLAN_KEY_MGMT_PSK) 11008c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_WPA_PSK; 11018c2ecf20Sopenharmony_ci else 11028c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_WPA_NONE; 11038c2ecf20Sopenharmony_ci } else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) 11048c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_SHARED; 11058c2ecf20Sopenharmony_ci else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) 11068c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_OPEN; 11078c2ecf20Sopenharmony_ci else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC) 11088c2ecf20Sopenharmony_ci auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; 11098c2ecf20Sopenharmony_ci else 11108c2ecf20Sopenharmony_ci return -ENOTSUPP; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci tmp = cpu_to_le32(auth_mode); 11138c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 11148c2ecf20Sopenharmony_ci RNDIS_OID_802_11_AUTHENTICATION_MODE, 11158c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 11168c2ecf20Sopenharmony_ci if (ret != 0) { 11178c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "setting auth mode failed (%08X)\n", 11188c2ecf20Sopenharmony_ci ret); 11198c2ecf20Sopenharmony_ci return ret; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci priv->wpa_version = wpa_version; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic int set_priv_filter(struct usbnet *usbdev) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 11308c2ecf20Sopenharmony_ci __le32 tmp; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x\n", 11338c2ecf20Sopenharmony_ci __func__, priv->wpa_version); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (priv->wpa_version & NL80211_WPA_VERSION_2 || 11368c2ecf20Sopenharmony_ci priv->wpa_version & NL80211_WPA_VERSION_1) 11378c2ecf20Sopenharmony_ci tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); 11388c2ecf20Sopenharmony_ci else 11398c2ecf20Sopenharmony_ci tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return rndis_set_oid(usbdev, 11428c2ecf20Sopenharmony_ci RNDIS_OID_802_11_PRIVACY_FILTER, &tmp, 11438c2ecf20Sopenharmony_ci sizeof(tmp)); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci __le32 tmp; 11498c2ecf20Sopenharmony_ci int encr_mode, ret; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): cipher_pair=0x%x cipher_group=0x%x\n", 11528c2ecf20Sopenharmony_ci __func__, pairwise, groupwise); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (pairwise & RNDIS_WLAN_ALG_CCMP) 11558c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; 11568c2ecf20Sopenharmony_ci else if (pairwise & RNDIS_WLAN_ALG_TKIP) 11578c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; 11588c2ecf20Sopenharmony_ci else if (pairwise & RNDIS_WLAN_ALG_WEP) 11598c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_WEP_ENABLED; 11608c2ecf20Sopenharmony_ci else if (groupwise & RNDIS_WLAN_ALG_CCMP) 11618c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; 11628c2ecf20Sopenharmony_ci else if (groupwise & RNDIS_WLAN_ALG_TKIP) 11638c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; 11648c2ecf20Sopenharmony_ci else 11658c2ecf20Sopenharmony_ci encr_mode = NDIS_80211_ENCR_DISABLED; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci tmp = cpu_to_le32(encr_mode); 11688c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 11698c2ecf20Sopenharmony_ci RNDIS_OID_802_11_ENCRYPTION_STATUS, &tmp, 11708c2ecf20Sopenharmony_ci sizeof(tmp)); 11718c2ecf20Sopenharmony_ci if (ret != 0) { 11728c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "setting encr mode failed (%08X)\n", 11738c2ecf20Sopenharmony_ci ret); 11748c2ecf20Sopenharmony_ci return ret; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic int set_infra_mode(struct usbnet *usbdev, int mode) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 11838c2ecf20Sopenharmony_ci __le32 tmp; 11848c2ecf20Sopenharmony_ci int ret; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): infra_mode=0x%x\n", 11878c2ecf20Sopenharmony_ci __func__, priv->infra_mode); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci tmp = cpu_to_le32(mode); 11908c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 11918c2ecf20Sopenharmony_ci RNDIS_OID_802_11_INFRASTRUCTURE_MODE, 11928c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 11938c2ecf20Sopenharmony_ci if (ret != 0) { 11948c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "setting infra mode failed (%08X)\n", 11958c2ecf20Sopenharmony_ci ret); 11968c2ecf20Sopenharmony_ci return ret; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* NDIS drivers clear keys when infrastructure mode is 12008c2ecf20Sopenharmony_ci * changed. But Linux tools assume otherwise. So set the 12018c2ecf20Sopenharmony_ci * keys */ 12028c2ecf20Sopenharmony_ci restore_keys(usbdev); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci priv->infra_mode = mode; 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci __le32 tmp; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (rts_threshold == -1 || rts_threshold > 2347) 12158c2ecf20Sopenharmony_ci rts_threshold = 2347; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci tmp = cpu_to_le32(rts_threshold); 12188c2ecf20Sopenharmony_ci return rndis_set_oid(usbdev, 12198c2ecf20Sopenharmony_ci RNDIS_OID_802_11_RTS_THRESHOLD, 12208c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci __le32 tmp; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %i\n", __func__, frag_threshold); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (frag_threshold < 256 || frag_threshold > 2346) 12308c2ecf20Sopenharmony_ci frag_threshold = 2346; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci tmp = cpu_to_le32(frag_threshold); 12338c2ecf20Sopenharmony_ci return rndis_set_oid(usbdev, 12348c2ecf20Sopenharmony_ci RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD, 12358c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic void set_default_iw_params(struct usbnet *usbdev) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); 12418c2ecf20Sopenharmony_ci set_auth_mode(usbdev, 0, NL80211_AUTHTYPE_OPEN_SYSTEM, 12428c2ecf20Sopenharmony_ci RNDIS_WLAN_KEY_MGMT_NONE); 12438c2ecf20Sopenharmony_ci set_priv_filter(usbdev); 12448c2ecf20Sopenharmony_ci set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int deauthenticate(struct usbnet *usbdev) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci int ret; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci ret = disassociate(usbdev, true); 12528c2ecf20Sopenharmony_ci set_default_iw_params(usbdev); 12538c2ecf20Sopenharmony_ci return ret; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic int set_channel(struct usbnet *usbdev, int channel) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct ndis_80211_conf config; 12598c2ecf20Sopenharmony_ci unsigned int dsconfig; 12608c2ecf20Sopenharmony_ci int len, ret; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%d)\n", __func__, channel); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* this OID is valid only when not associated */ 12658c2ecf20Sopenharmony_ci if (is_associated(usbdev)) 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci dsconfig = 1000 * 12698c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci len = sizeof(config); 12728c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, 12738c2ecf20Sopenharmony_ci RNDIS_OID_802_11_CONFIGURATION, 12748c2ecf20Sopenharmony_ci &config, &len); 12758c2ecf20Sopenharmony_ci if (ret < 0) { 12768c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): querying configuration failed\n", 12778c2ecf20Sopenharmony_ci __func__); 12788c2ecf20Sopenharmony_ci return ret; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci config.ds_config = cpu_to_le32(dsconfig); 12828c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 12838c2ecf20Sopenharmony_ci RNDIS_OID_802_11_CONFIGURATION, 12848c2ecf20Sopenharmony_ci &config, sizeof(config)); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %d -> %d\n", __func__, channel, ret); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci return ret; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic struct ieee80211_channel *get_current_channel(struct usbnet *usbdev, 12928c2ecf20Sopenharmony_ci u32 *beacon_period) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 12958c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 12968c2ecf20Sopenharmony_ci struct ndis_80211_conf config; 12978c2ecf20Sopenharmony_ci int len, ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* Get channel and beacon interval */ 13008c2ecf20Sopenharmony_ci len = sizeof(config); 13018c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, 13028c2ecf20Sopenharmony_ci RNDIS_OID_802_11_CONFIGURATION, 13038c2ecf20Sopenharmony_ci &config, &len); 13048c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_CONFIGURATION -> %d\n", 13058c2ecf20Sopenharmony_ci __func__, ret); 13068c2ecf20Sopenharmony_ci if (ret < 0) 13078c2ecf20Sopenharmony_ci return NULL; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci channel = ieee80211_get_channel(priv->wdev.wiphy, 13108c2ecf20Sopenharmony_ci KHZ_TO_MHZ(le32_to_cpu(config.ds_config))); 13118c2ecf20Sopenharmony_ci if (!channel) 13128c2ecf20Sopenharmony_ci return NULL; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (beacon_period) 13158c2ecf20Sopenharmony_ci *beacon_period = le32_to_cpu(config.beacon_period); 13168c2ecf20Sopenharmony_ci return channel; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci/* index must be 0 - N, as per NDIS */ 13208c2ecf20Sopenharmony_cistatic int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len, 13218c2ecf20Sopenharmony_ci u8 index) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 13248c2ecf20Sopenharmony_ci struct ndis_80211_wep_key ndis_key; 13258c2ecf20Sopenharmony_ci u32 cipher; 13268c2ecf20Sopenharmony_ci int ret; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(idx: %d, len: %d)\n", 13298c2ecf20Sopenharmony_ci __func__, index, key_len); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci if (index >= RNDIS_WLAN_NUM_KEYS) 13328c2ecf20Sopenharmony_ci return -EINVAL; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci if (key_len == 5) 13358c2ecf20Sopenharmony_ci cipher = WLAN_CIPHER_SUITE_WEP40; 13368c2ecf20Sopenharmony_ci else if (key_len == 13) 13378c2ecf20Sopenharmony_ci cipher = WLAN_CIPHER_SUITE_WEP104; 13388c2ecf20Sopenharmony_ci else 13398c2ecf20Sopenharmony_ci return -EINVAL; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci memset(&ndis_key, 0, sizeof(ndis_key)); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci ndis_key.size = cpu_to_le32(sizeof(ndis_key)); 13448c2ecf20Sopenharmony_ci ndis_key.length = cpu_to_le32(key_len); 13458c2ecf20Sopenharmony_ci ndis_key.index = cpu_to_le32(index); 13468c2ecf20Sopenharmony_ci memcpy(&ndis_key.material, key, key_len); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (index == priv->encr_tx_key_index) { 13498c2ecf20Sopenharmony_ci ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; 13508c2ecf20Sopenharmony_ci ret = set_encr_mode(usbdev, RNDIS_WLAN_ALG_WEP, 13518c2ecf20Sopenharmony_ci RNDIS_WLAN_ALG_NONE); 13528c2ecf20Sopenharmony_ci if (ret) 13538c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "encryption couldn't be enabled (%08X)\n", 13548c2ecf20Sopenharmony_ci ret); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 13588c2ecf20Sopenharmony_ci RNDIS_OID_802_11_ADD_WEP, &ndis_key, 13598c2ecf20Sopenharmony_ci sizeof(ndis_key)); 13608c2ecf20Sopenharmony_ci if (ret != 0) { 13618c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "adding encryption key %d failed (%08X)\n", 13628c2ecf20Sopenharmony_ci index + 1, ret); 13638c2ecf20Sopenharmony_ci return ret; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci priv->encr_keys[index].len = key_len; 13678c2ecf20Sopenharmony_ci priv->encr_keys[index].cipher = cipher; 13688c2ecf20Sopenharmony_ci memcpy(&priv->encr_keys[index].material, key, key_len); 13698c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->encr_keys[index].bssid); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return 0; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, 13758c2ecf20Sopenharmony_ci u8 index, const u8 *addr, const u8 *rx_seq, 13768c2ecf20Sopenharmony_ci int seq_len, u32 cipher, __le32 flags) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 13798c2ecf20Sopenharmony_ci struct ndis_80211_key ndis_key; 13808c2ecf20Sopenharmony_ci bool is_addr_ok; 13818c2ecf20Sopenharmony_ci int ret; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (index >= RNDIS_WLAN_NUM_KEYS) { 13848c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): index out of range (%i)\n", 13858c2ecf20Sopenharmony_ci __func__, index); 13868c2ecf20Sopenharmony_ci return -EINVAL; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci if (key_len > sizeof(ndis_key.material) || key_len < 0) { 13898c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): key length out of range (%i)\n", 13908c2ecf20Sopenharmony_ci __func__, key_len); 13918c2ecf20Sopenharmony_ci return -EINVAL; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) { 13948c2ecf20Sopenharmony_ci if (!rx_seq || seq_len <= 0) { 13958c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): recv seq flag without buffer\n", 13968c2ecf20Sopenharmony_ci __func__); 13978c2ecf20Sopenharmony_ci return -EINVAL; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci if (rx_seq && seq_len > sizeof(ndis_key.rsc)) { 14008c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): too big recv seq buffer\n", __func__); 14018c2ecf20Sopenharmony_ci return -EINVAL; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci is_addr_ok = addr && !is_zero_ether_addr(addr) && 14068c2ecf20Sopenharmony_ci !is_broadcast_ether_addr(addr); 14078c2ecf20Sopenharmony_ci if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) { 14088c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): pairwise but bssid invalid (%pM)\n", 14098c2ecf20Sopenharmony_ci __func__, addr); 14108c2ecf20Sopenharmony_ci return -EINVAL; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%i): flags:%i%i%i\n", 14148c2ecf20Sopenharmony_ci __func__, index, 14158c2ecf20Sopenharmony_ci !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), 14168c2ecf20Sopenharmony_ci !!(flags & NDIS_80211_ADDKEY_PAIRWISE_KEY), 14178c2ecf20Sopenharmony_ci !!(flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci memset(&ndis_key, 0, sizeof(ndis_key)); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci ndis_key.size = cpu_to_le32(sizeof(ndis_key) - 14228c2ecf20Sopenharmony_ci sizeof(ndis_key.material) + key_len); 14238c2ecf20Sopenharmony_ci ndis_key.length = cpu_to_le32(key_len); 14248c2ecf20Sopenharmony_ci ndis_key.index = cpu_to_le32(index) | flags; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) { 14278c2ecf20Sopenharmony_ci /* wpa_supplicant gives us the Michael MIC RX/TX keys in 14288c2ecf20Sopenharmony_ci * different order than NDIS spec, so swap the order here. */ 14298c2ecf20Sopenharmony_ci memcpy(ndis_key.material, key, 16); 14308c2ecf20Sopenharmony_ci memcpy(ndis_key.material + 16, key + 24, 8); 14318c2ecf20Sopenharmony_ci memcpy(ndis_key.material + 24, key + 16, 8); 14328c2ecf20Sopenharmony_ci } else 14338c2ecf20Sopenharmony_ci memcpy(ndis_key.material, key, key_len); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) 14368c2ecf20Sopenharmony_ci memcpy(ndis_key.rsc, rx_seq, seq_len); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { 14398c2ecf20Sopenharmony_ci /* pairwise key */ 14408c2ecf20Sopenharmony_ci memcpy(ndis_key.bssid, addr, ETH_ALEN); 14418c2ecf20Sopenharmony_ci } else { 14428c2ecf20Sopenharmony_ci /* group key */ 14438c2ecf20Sopenharmony_ci if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) 14448c2ecf20Sopenharmony_ci eth_broadcast_addr(ndis_key.bssid); 14458c2ecf20Sopenharmony_ci else 14468c2ecf20Sopenharmony_ci get_bssid(usbdev, ndis_key.bssid); 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 14508c2ecf20Sopenharmony_ci RNDIS_OID_802_11_ADD_KEY, &ndis_key, 14518c2ecf20Sopenharmony_ci le32_to_cpu(ndis_key.size)); 14528c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_ADD_KEY -> %08X\n", 14538c2ecf20Sopenharmony_ci __func__, ret); 14548c2ecf20Sopenharmony_ci if (ret != 0) 14558c2ecf20Sopenharmony_ci return ret; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); 14588c2ecf20Sopenharmony_ci priv->encr_keys[index].len = key_len; 14598c2ecf20Sopenharmony_ci priv->encr_keys[index].cipher = cipher; 14608c2ecf20Sopenharmony_ci memcpy(&priv->encr_keys[index].material, key, key_len); 14618c2ecf20Sopenharmony_ci if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) 14628c2ecf20Sopenharmony_ci memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN); 14638c2ecf20Sopenharmony_ci else 14648c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->encr_keys[index].bssid); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) 14678c2ecf20Sopenharmony_ci priv->encr_tx_key_index = index; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return 0; 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic int restore_key(struct usbnet *usbdev, u8 key_idx) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 14758c2ecf20Sopenharmony_ci struct rndis_wlan_encr_key key; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (is_wpa_key(priv, key_idx)) 14788c2ecf20Sopenharmony_ci return 0; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci key = priv->encr_keys[key_idx]; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %i:%i\n", __func__, key_idx, key.len); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (key.len == 0) 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci return add_wep_key(usbdev, key.material, key.len, key_idx); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void restore_keys(struct usbnet *usbdev) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci int i; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 14958c2ecf20Sopenharmony_ci restore_key(usbdev, i); 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic void clear_key(struct rndis_wlan_private *priv, u8 idx) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx])); 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci/* remove_key is for both wep and wpa */ 15048c2ecf20Sopenharmony_cistatic int remove_key(struct usbnet *usbdev, u8 index, const u8 *bssid) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 15078c2ecf20Sopenharmony_ci struct ndis_80211_remove_key remove_key; 15088c2ecf20Sopenharmony_ci __le32 keyindex; 15098c2ecf20Sopenharmony_ci bool is_wpa; 15108c2ecf20Sopenharmony_ci int ret; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (index >= RNDIS_WLAN_NUM_KEYS) 15138c2ecf20Sopenharmony_ci return -ENOENT; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (priv->encr_keys[index].len == 0) 15168c2ecf20Sopenharmony_ci return 0; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci is_wpa = is_wpa_key(priv, index); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %i:%s:%i\n", 15218c2ecf20Sopenharmony_ci __func__, index, is_wpa ? "wpa" : "wep", 15228c2ecf20Sopenharmony_ci priv->encr_keys[index].len); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci clear_key(priv, index); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci if (is_wpa) { 15278c2ecf20Sopenharmony_ci remove_key.size = cpu_to_le32(sizeof(remove_key)); 15288c2ecf20Sopenharmony_ci remove_key.index = cpu_to_le32(index); 15298c2ecf20Sopenharmony_ci if (bssid) { 15308c2ecf20Sopenharmony_ci /* pairwise key */ 15318c2ecf20Sopenharmony_ci if (!is_broadcast_ether_addr(bssid)) 15328c2ecf20Sopenharmony_ci remove_key.index |= 15338c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_PAIRWISE_KEY; 15348c2ecf20Sopenharmony_ci memcpy(remove_key.bssid, bssid, 15358c2ecf20Sopenharmony_ci sizeof(remove_key.bssid)); 15368c2ecf20Sopenharmony_ci } else 15378c2ecf20Sopenharmony_ci memset(remove_key.bssid, 0xff, 15388c2ecf20Sopenharmony_ci sizeof(remove_key.bssid)); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 15418c2ecf20Sopenharmony_ci RNDIS_OID_802_11_REMOVE_KEY, 15428c2ecf20Sopenharmony_ci &remove_key, sizeof(remove_key)); 15438c2ecf20Sopenharmony_ci if (ret != 0) 15448c2ecf20Sopenharmony_ci return ret; 15458c2ecf20Sopenharmony_ci } else { 15468c2ecf20Sopenharmony_ci keyindex = cpu_to_le32(index); 15478c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 15488c2ecf20Sopenharmony_ci RNDIS_OID_802_11_REMOVE_WEP, 15498c2ecf20Sopenharmony_ci &keyindex, sizeof(keyindex)); 15508c2ecf20Sopenharmony_ci if (ret != 0) { 15518c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, 15528c2ecf20Sopenharmony_ci "removing encryption key %d failed (%08X)\n", 15538c2ecf20Sopenharmony_ci index, ret); 15548c2ecf20Sopenharmony_ci return ret; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* if it is transmit key, disable encryption */ 15598c2ecf20Sopenharmony_ci if (index == priv->encr_tx_key_index) 15608c2ecf20Sopenharmony_ci set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic void set_multicast_list(struct usbnet *usbdev) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 15688c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 15698c2ecf20Sopenharmony_ci __le32 filter, basefilter; 15708c2ecf20Sopenharmony_ci int ret; 15718c2ecf20Sopenharmony_ci char *mc_addrs = NULL; 15728c2ecf20Sopenharmony_ci int mc_count; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci basefilter = filter = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | 15758c2ecf20Sopenharmony_ci RNDIS_PACKET_TYPE_BROADCAST); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (usbdev->net->flags & IFF_PROMISC) { 15788c2ecf20Sopenharmony_ci filter |= cpu_to_le32(RNDIS_PACKET_TYPE_PROMISCUOUS | 15798c2ecf20Sopenharmony_ci RNDIS_PACKET_TYPE_ALL_LOCAL); 15808c2ecf20Sopenharmony_ci } else if (usbdev->net->flags & IFF_ALLMULTI) { 15818c2ecf20Sopenharmony_ci filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (filter != basefilter) 15858c2ecf20Sopenharmony_ci goto set_filter; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* 15888c2ecf20Sopenharmony_ci * mc_list should be accessed holding the lock, so copy addresses to 15898c2ecf20Sopenharmony_ci * local buffer first. 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_ci netif_addr_lock_bh(usbdev->net); 15928c2ecf20Sopenharmony_ci mc_count = netdev_mc_count(usbdev->net); 15938c2ecf20Sopenharmony_ci if (mc_count > priv->multicast_size) { 15948c2ecf20Sopenharmony_ci filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); 15958c2ecf20Sopenharmony_ci } else if (mc_count) { 15968c2ecf20Sopenharmony_ci int i = 0; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci mc_addrs = kmalloc_array(mc_count, ETH_ALEN, GFP_ATOMIC); 15998c2ecf20Sopenharmony_ci if (!mc_addrs) { 16008c2ecf20Sopenharmony_ci netif_addr_unlock_bh(usbdev->net); 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, usbdev->net) 16058c2ecf20Sopenharmony_ci memcpy(mc_addrs + i++ * ETH_ALEN, 16068c2ecf20Sopenharmony_ci ha->addr, ETH_ALEN); 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci netif_addr_unlock_bh(usbdev->net); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (filter != basefilter) 16118c2ecf20Sopenharmony_ci goto set_filter; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (mc_count) { 16148c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, 16158c2ecf20Sopenharmony_ci RNDIS_OID_802_3_MULTICAST_LIST, 16168c2ecf20Sopenharmony_ci mc_addrs, mc_count * ETH_ALEN); 16178c2ecf20Sopenharmony_ci kfree(mc_addrs); 16188c2ecf20Sopenharmony_ci if (ret == 0) 16198c2ecf20Sopenharmony_ci filter |= cpu_to_le32(RNDIS_PACKET_TYPE_MULTICAST); 16208c2ecf20Sopenharmony_ci else 16218c2ecf20Sopenharmony_ci filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "RNDIS_OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", 16248c2ecf20Sopenharmony_ci mc_count, priv->multicast_size, ret); 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ciset_filter: 16288c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, 16298c2ecf20Sopenharmony_ci sizeof(filter)); 16308c2ecf20Sopenharmony_ci if (ret < 0) { 16318c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "couldn't set packet filter: %08x\n", 16328c2ecf20Sopenharmony_ci le32_to_cpu(filter)); 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "RNDIS_OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d\n", 16368c2ecf20Sopenharmony_ci le32_to_cpu(filter), ret); 16378c2ecf20Sopenharmony_ci} 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci#ifdef DEBUG 16408c2ecf20Sopenharmony_cistatic void debug_print_pmkids(struct usbnet *usbdev, 16418c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids, 16428c2ecf20Sopenharmony_ci const char *func_str) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 16458c2ecf20Sopenharmony_ci int i, len, count, max_pmkids, entry_len; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci max_pmkids = priv->wdev.wiphy->max_num_pmkids; 16488c2ecf20Sopenharmony_ci len = le32_to_cpu(pmkids->length); 16498c2ecf20Sopenharmony_ci count = le32_to_cpu(pmkids->bssid_info_count); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci entry_len = (count > 0) ? (len - sizeof(*pmkids)) / count : -1; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %d PMKIDs (data len: %d, entry len: " 16548c2ecf20Sopenharmony_ci "%d)\n", func_str, count, len, entry_len); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci if (count > max_pmkids) 16578c2ecf20Sopenharmony_ci count = max_pmkids; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 16608c2ecf20Sopenharmony_ci u32 *tmp = (u32 *)pmkids->bssid_info[i].pmkid; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): bssid: %pM, " 16638c2ecf20Sopenharmony_ci "pmkid: %08X:%08X:%08X:%08X\n", 16648c2ecf20Sopenharmony_ci func_str, pmkids->bssid_info[i].bssid, 16658c2ecf20Sopenharmony_ci cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), 16668c2ecf20Sopenharmony_ci cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci#else 16708c2ecf20Sopenharmony_cistatic void debug_print_pmkids(struct usbnet *usbdev, 16718c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids, 16728c2ecf20Sopenharmony_ci const char *func_str) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci return; 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci#endif 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic struct ndis_80211_pmkid *get_device_pmkids(struct usbnet *usbdev) 16798c2ecf20Sopenharmony_ci{ 16808c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 16818c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids; 16828c2ecf20Sopenharmony_ci int len, ret, max_pmkids; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci max_pmkids = priv->wdev.wiphy->max_num_pmkids; 16858c2ecf20Sopenharmony_ci len = struct_size(pmkids, bssid_info, max_pmkids); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci pmkids = kzalloc(len, GFP_KERNEL); 16888c2ecf20Sopenharmony_ci if (!pmkids) 16898c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci pmkids->length = cpu_to_le32(len); 16928c2ecf20Sopenharmony_ci pmkids->bssid_info_count = cpu_to_le32(max_pmkids); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_PMKID, 16958c2ecf20Sopenharmony_ci pmkids, &len); 16968c2ecf20Sopenharmony_ci if (ret < 0) { 16978c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d)" 16988c2ecf20Sopenharmony_ci " -> %d\n", __func__, len, max_pmkids, ret); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci kfree(pmkids); 17018c2ecf20Sopenharmony_ci return ERR_PTR(ret); 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (le32_to_cpu(pmkids->bssid_info_count) > max_pmkids) 17058c2ecf20Sopenharmony_ci pmkids->bssid_info_count = cpu_to_le32(max_pmkids); 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci debug_print_pmkids(usbdev, pmkids, __func__); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci return pmkids; 17108c2ecf20Sopenharmony_ci} 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_cistatic int set_device_pmkids(struct usbnet *usbdev, 17138c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci int ret, len, num_pmkids; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci num_pmkids = le32_to_cpu(pmkids->bssid_info_count); 17188c2ecf20Sopenharmony_ci len = struct_size(pmkids, bssid_info, num_pmkids); 17198c2ecf20Sopenharmony_ci pmkids->length = cpu_to_le32(len); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci debug_print_pmkids(usbdev, pmkids, __func__); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, pmkids, 17248c2ecf20Sopenharmony_ci le32_to_cpu(pmkids->length)); 17258c2ecf20Sopenharmony_ci if (ret < 0) { 17268c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d) -> %d" 17278c2ecf20Sopenharmony_ci "\n", __func__, len, num_pmkids, ret); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci kfree(pmkids); 17318c2ecf20Sopenharmony_ci return ret; 17328c2ecf20Sopenharmony_ci} 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_cistatic struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev, 17358c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids, 17368c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa, 17378c2ecf20Sopenharmony_ci int max_pmkids) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci int i, err; 17408c2ecf20Sopenharmony_ci unsigned int count; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci count = le32_to_cpu(pmkids->bssid_info_count); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (count > max_pmkids) 17458c2ecf20Sopenharmony_ci count = max_pmkids; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 17488c2ecf20Sopenharmony_ci if (ether_addr_equal(pmkids->bssid_info[i].bssid, 17498c2ecf20Sopenharmony_ci pmksa->bssid)) 17508c2ecf20Sopenharmony_ci break; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci /* pmkid not found */ 17538c2ecf20Sopenharmony_ci if (i == count) { 17548c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): bssid not found (%pM)\n", 17558c2ecf20Sopenharmony_ci __func__, pmksa->bssid); 17568c2ecf20Sopenharmony_ci err = -ENOENT; 17578c2ecf20Sopenharmony_ci goto error; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci for (; i + 1 < count; i++) 17618c2ecf20Sopenharmony_ci pmkids->bssid_info[i] = pmkids->bssid_info[i + 1]; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci count--; 17648c2ecf20Sopenharmony_ci pmkids->length = cpu_to_le32(struct_size(pmkids, bssid_info, count)); 17658c2ecf20Sopenharmony_ci pmkids->bssid_info_count = cpu_to_le32(count); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return pmkids; 17688c2ecf20Sopenharmony_cierror: 17698c2ecf20Sopenharmony_ci kfree(pmkids); 17708c2ecf20Sopenharmony_ci return ERR_PTR(err); 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, 17748c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids, 17758c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa, 17768c2ecf20Sopenharmony_ci int max_pmkids) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *new_pmkids; 17798c2ecf20Sopenharmony_ci int i, err, newlen; 17808c2ecf20Sopenharmony_ci unsigned int count; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci count = le32_to_cpu(pmkids->bssid_info_count); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (count > max_pmkids) 17858c2ecf20Sopenharmony_ci count = max_pmkids; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* update with new pmkid */ 17888c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 17898c2ecf20Sopenharmony_ci if (!ether_addr_equal(pmkids->bssid_info[i].bssid, 17908c2ecf20Sopenharmony_ci pmksa->bssid)) 17918c2ecf20Sopenharmony_ci continue; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci memcpy(pmkids->bssid_info[i].pmkid, pmksa->pmkid, 17948c2ecf20Sopenharmony_ci WLAN_PMKID_LEN); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci return pmkids; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* out of space, return error */ 18008c2ecf20Sopenharmony_ci if (i == max_pmkids) { 18018c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): out of space\n", __func__); 18028c2ecf20Sopenharmony_ci err = -ENOSPC; 18038c2ecf20Sopenharmony_ci goto error; 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci /* add new pmkid */ 18078c2ecf20Sopenharmony_ci newlen = struct_size(pmkids, bssid_info, count + 1); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL); 18108c2ecf20Sopenharmony_ci if (!new_pmkids) { 18118c2ecf20Sopenharmony_ci err = -ENOMEM; 18128c2ecf20Sopenharmony_ci goto error; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci pmkids = new_pmkids; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci pmkids->length = cpu_to_le32(newlen); 18178c2ecf20Sopenharmony_ci pmkids->bssid_info_count = cpu_to_le32(count + 1); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci memcpy(pmkids->bssid_info[count].bssid, pmksa->bssid, ETH_ALEN); 18208c2ecf20Sopenharmony_ci memcpy(pmkids->bssid_info[count].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci return pmkids; 18238c2ecf20Sopenharmony_cierror: 18248c2ecf20Sopenharmony_ci kfree(pmkids); 18258c2ecf20Sopenharmony_ci return ERR_PTR(err); 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci/* 18298c2ecf20Sopenharmony_ci * cfg80211 ops 18308c2ecf20Sopenharmony_ci */ 18318c2ecf20Sopenharmony_cistatic int rndis_change_virtual_intf(struct wiphy *wiphy, 18328c2ecf20Sopenharmony_ci struct net_device *dev, 18338c2ecf20Sopenharmony_ci enum nl80211_iftype type, 18348c2ecf20Sopenharmony_ci struct vif_params *params) 18358c2ecf20Sopenharmony_ci{ 18368c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 18378c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 18388c2ecf20Sopenharmony_ci int mode; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci switch (type) { 18418c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 18428c2ecf20Sopenharmony_ci mode = NDIS_80211_INFRA_ADHOC; 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 18458c2ecf20Sopenharmony_ci mode = NDIS_80211_INFRA_INFRA; 18468c2ecf20Sopenharmony_ci break; 18478c2ecf20Sopenharmony_ci default: 18488c2ecf20Sopenharmony_ci return -EINVAL; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci priv->wdev.iftype = type; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci return set_infra_mode(usbdev, mode); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 18598c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 18608c2ecf20Sopenharmony_ci int err; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { 18638c2ecf20Sopenharmony_ci err = set_frag_threshold(usbdev, wiphy->frag_threshold); 18648c2ecf20Sopenharmony_ci if (err < 0) 18658c2ecf20Sopenharmony_ci return err; 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (changed & WIPHY_PARAM_RTS_THRESHOLD) { 18698c2ecf20Sopenharmony_ci err = set_rts_threshold(usbdev, wiphy->rts_threshold); 18708c2ecf20Sopenharmony_ci if (err < 0) 18718c2ecf20Sopenharmony_ci return err; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci return 0; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic int rndis_set_tx_power(struct wiphy *wiphy, 18788c2ecf20Sopenharmony_ci struct wireless_dev *wdev, 18798c2ecf20Sopenharmony_ci enum nl80211_tx_power_setting type, 18808c2ecf20Sopenharmony_ci int mbm) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 18838c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): type:0x%x mbm:%i\n", 18868c2ecf20Sopenharmony_ci __func__, type, mbm); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (mbm < 0 || (mbm % 100)) 18898c2ecf20Sopenharmony_ci return -ENOTSUPP; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci /* Device doesn't support changing txpower after initialization, only 18928c2ecf20Sopenharmony_ci * turn off/on radio. Support 'auto' mode and setting same dBm that is 18938c2ecf20Sopenharmony_ci * currently used. 18948c2ecf20Sopenharmony_ci */ 18958c2ecf20Sopenharmony_ci if (type == NL80211_TX_POWER_AUTOMATIC || 18968c2ecf20Sopenharmony_ci MBM_TO_DBM(mbm) == get_bcm4320_power_dbm(priv)) { 18978c2ecf20Sopenharmony_ci if (!priv->radio_on) 18988c2ecf20Sopenharmony_ci disassociate(usbdev, true); /* turn on radio */ 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci return 0; 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci return -ENOTSUPP; 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic int rndis_get_tx_power(struct wiphy *wiphy, 19078c2ecf20Sopenharmony_ci struct wireless_dev *wdev, 19088c2ecf20Sopenharmony_ci int *dbm) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 19118c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci *dbm = get_bcm4320_power_dbm(priv); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): dbm:%i\n", __func__, *dbm); 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci return 0; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci#define SCAN_DELAY_JIFFIES (6 * HZ) 19218c2ecf20Sopenharmony_cistatic int rndis_scan(struct wiphy *wiphy, 19228c2ecf20Sopenharmony_ci struct cfg80211_scan_request *request) 19238c2ecf20Sopenharmony_ci{ 19248c2ecf20Sopenharmony_ci struct net_device *dev = request->wdev->netdev; 19258c2ecf20Sopenharmony_ci struct usbnet *usbdev = netdev_priv(dev); 19268c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 19278c2ecf20Sopenharmony_ci int ret; 19288c2ecf20Sopenharmony_ci int delay = SCAN_DELAY_JIFFIES; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cfg80211.scan\n"); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* Get current bssid list from device before new scan, as new scan 19338c2ecf20Sopenharmony_ci * clears internal bssid list. 19348c2ecf20Sopenharmony_ci */ 19358c2ecf20Sopenharmony_ci rndis_check_bssid_list(usbdev, NULL, NULL); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (priv->scan_request && priv->scan_request != request) 19388c2ecf20Sopenharmony_ci return -EBUSY; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci priv->scan_request = request; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci ret = rndis_start_bssid_list_scan(usbdev); 19438c2ecf20Sopenharmony_ci if (ret == 0) { 19448c2ecf20Sopenharmony_ci if (priv->device_type == RNDIS_BCM4320A) 19458c2ecf20Sopenharmony_ci delay = HZ; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci /* Wait before retrieving scan results from device */ 19488c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->scan_work, delay); 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci return ret; 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic bool rndis_bss_info_update(struct usbnet *usbdev, 19558c2ecf20Sopenharmony_ci struct ndis_80211_bssid_ex *bssid) 19568c2ecf20Sopenharmony_ci{ 19578c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 19588c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 19598c2ecf20Sopenharmony_ci struct cfg80211_bss *bss; 19608c2ecf20Sopenharmony_ci s32 signal; 19618c2ecf20Sopenharmony_ci u64 timestamp; 19628c2ecf20Sopenharmony_ci u16 capability; 19638c2ecf20Sopenharmony_ci u16 beacon_interval; 19648c2ecf20Sopenharmony_ci struct ndis_80211_fixed_ies *fixed; 19658c2ecf20Sopenharmony_ci int ie_len, bssid_len; 19668c2ecf20Sopenharmony_ci u8 *ie; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n", 19698c2ecf20Sopenharmony_ci bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length)); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* parse bssid structure */ 19728c2ecf20Sopenharmony_ci bssid_len = le32_to_cpu(bssid->length); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (bssid_len < sizeof(struct ndis_80211_bssid_ex) + 19758c2ecf20Sopenharmony_ci sizeof(struct ndis_80211_fixed_ies)) 19768c2ecf20Sopenharmony_ci return NULL; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci fixed = (struct ndis_80211_fixed_ies *)bssid->ies; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); 19818c2ecf20Sopenharmony_ci ie_len = min(bssid_len - (int)sizeof(*bssid), 19828c2ecf20Sopenharmony_ci (int)le32_to_cpu(bssid->ie_length)); 19838c2ecf20Sopenharmony_ci ie_len -= sizeof(struct ndis_80211_fixed_ies); 19848c2ecf20Sopenharmony_ci if (ie_len < 0) 19858c2ecf20Sopenharmony_ci return NULL; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* extract data for cfg80211_inform_bss */ 19888c2ecf20Sopenharmony_ci channel = ieee80211_get_channel(priv->wdev.wiphy, 19898c2ecf20Sopenharmony_ci KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config))); 19908c2ecf20Sopenharmony_ci if (!channel) 19918c2ecf20Sopenharmony_ci return NULL; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci signal = level_to_qual(le32_to_cpu(bssid->rssi)); 19948c2ecf20Sopenharmony_ci timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp); 19958c2ecf20Sopenharmony_ci capability = le16_to_cpu(fixed->capabilities); 19968c2ecf20Sopenharmony_ci beacon_interval = le16_to_cpu(fixed->beacon_interval); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, 19998c2ecf20Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, bssid->mac, 20008c2ecf20Sopenharmony_ci timestamp, capability, beacon_interval, 20018c2ecf20Sopenharmony_ci ie, ie_len, signal, GFP_KERNEL); 20028c2ecf20Sopenharmony_ci cfg80211_put_bss(priv->wdev.wiphy, bss); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return (bss != NULL); 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_cistatic struct ndis_80211_bssid_ex *next_bssid_list_item( 20088c2ecf20Sopenharmony_ci struct ndis_80211_bssid_ex *bssid, 20098c2ecf20Sopenharmony_ci int *bssid_len, void *buf, int len) 20108c2ecf20Sopenharmony_ci{ 20118c2ecf20Sopenharmony_ci void *buf_end, *bssid_end; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci buf_end = (char *)buf + len; 20148c2ecf20Sopenharmony_ci bssid_end = (char *)bssid + *bssid_len; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) { 20178c2ecf20Sopenharmony_ci *bssid_len = 0; 20188c2ecf20Sopenharmony_ci return NULL; 20198c2ecf20Sopenharmony_ci } else { 20208c2ecf20Sopenharmony_ci bssid = (void *)((char *)bssid + *bssid_len); 20218c2ecf20Sopenharmony_ci *bssid_len = le32_to_cpu(bssid->length); 20228c2ecf20Sopenharmony_ci return bssid; 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_cistatic bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid, 20278c2ecf20Sopenharmony_ci int bssid_len, void *buf, int len) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci void *buf_end, *bssid_end; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci if (!bssid || bssid_len <= 0 || bssid_len > len) 20328c2ecf20Sopenharmony_ci return false; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci buf_end = (char *)buf + len; 20358c2ecf20Sopenharmony_ci bssid_end = (char *)bssid + bssid_len; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0; 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_cistatic int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, 20418c2ecf20Sopenharmony_ci bool *matched) 20428c2ecf20Sopenharmony_ci{ 20438c2ecf20Sopenharmony_ci void *buf = NULL; 20448c2ecf20Sopenharmony_ci struct ndis_80211_bssid_list_ex *bssid_list; 20458c2ecf20Sopenharmony_ci struct ndis_80211_bssid_ex *bssid; 20468c2ecf20Sopenharmony_ci int ret = -EINVAL, len, count, bssid_len, real_count, new_len; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s()\n", __func__); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci len = CONTROL_BUFFER_SIZE; 20518c2ecf20Sopenharmony_ciresize_buf: 20528c2ecf20Sopenharmony_ci buf = kzalloc(len, GFP_KERNEL); 20538c2ecf20Sopenharmony_ci if (!buf) { 20548c2ecf20Sopenharmony_ci ret = -ENOMEM; 20558c2ecf20Sopenharmony_ci goto out; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci /* BSSID-list might have got bigger last time we checked, keep 20598c2ecf20Sopenharmony_ci * resizing until it won't get any bigger. 20608c2ecf20Sopenharmony_ci */ 20618c2ecf20Sopenharmony_ci new_len = len; 20628c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST, 20638c2ecf20Sopenharmony_ci buf, &new_len); 20648c2ecf20Sopenharmony_ci if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex)) 20658c2ecf20Sopenharmony_ci goto out; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci if (new_len > len) { 20688c2ecf20Sopenharmony_ci len = new_len; 20698c2ecf20Sopenharmony_ci kfree(buf); 20708c2ecf20Sopenharmony_ci goto resize_buf; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci len = new_len; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci bssid_list = buf; 20768c2ecf20Sopenharmony_ci count = le32_to_cpu(bssid_list->num_items); 20778c2ecf20Sopenharmony_ci real_count = 0; 20788c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci bssid_len = 0; 20818c2ecf20Sopenharmony_ci bssid = next_bssid_list_item(bssid_list->bssid, &bssid_len, buf, len); 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci /* Device returns incorrect 'num_items'. Workaround by ignoring the 20848c2ecf20Sopenharmony_ci * received 'num_items' and walking through full bssid buffer instead. 20858c2ecf20Sopenharmony_ci */ 20868c2ecf20Sopenharmony_ci while (check_bssid_list_item(bssid, bssid_len, buf, len)) { 20878c2ecf20Sopenharmony_ci if (rndis_bss_info_update(usbdev, bssid) && match_bssid && 20888c2ecf20Sopenharmony_ci matched) { 20898c2ecf20Sopenharmony_ci if (ether_addr_equal(bssid->mac, match_bssid)) 20908c2ecf20Sopenharmony_ci *matched = true; 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci real_count++; 20948c2ecf20Sopenharmony_ci bssid = next_bssid_list_item(bssid, &bssid_len, buf, len); 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:" 20988c2ecf20Sopenharmony_ci " %d\n", __func__, count, real_count); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ciout: 21018c2ecf20Sopenharmony_ci kfree(buf); 21028c2ecf20Sopenharmony_ci return ret; 21038c2ecf20Sopenharmony_ci} 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_cistatic void rndis_get_scan_results(struct work_struct *work) 21068c2ecf20Sopenharmony_ci{ 21078c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = 21088c2ecf20Sopenharmony_ci container_of(work, struct rndis_wlan_private, scan_work.work); 21098c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 21108c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = {}; 21118c2ecf20Sopenharmony_ci int ret; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "get_scan_results\n"); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci if (!priv->scan_request) 21168c2ecf20Sopenharmony_ci return; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci ret = rndis_check_bssid_list(usbdev, NULL, NULL); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci info.aborted = ret < 0; 21218c2ecf20Sopenharmony_ci cfg80211_scan_done(priv->scan_request, &info); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci priv->scan_request = NULL; 21248c2ecf20Sopenharmony_ci} 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_cistatic int rndis_connect(struct wiphy *wiphy, struct net_device *dev, 21278c2ecf20Sopenharmony_ci struct cfg80211_connect_params *sme) 21288c2ecf20Sopenharmony_ci{ 21298c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 21308c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 21318c2ecf20Sopenharmony_ci struct ieee80211_channel *channel = sme->channel; 21328c2ecf20Sopenharmony_ci struct ndis_80211_ssid ssid; 21338c2ecf20Sopenharmony_ci int pairwise = RNDIS_WLAN_ALG_NONE; 21348c2ecf20Sopenharmony_ci int groupwise = RNDIS_WLAN_ALG_NONE; 21358c2ecf20Sopenharmony_ci int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE; 21368c2ecf20Sopenharmony_ci int length, i, ret, chan = -1; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (channel) 21398c2ecf20Sopenharmony_ci chan = ieee80211_frequency_to_channel(channel->center_freq); 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci groupwise = rndis_cipher_to_alg(sme->crypto.cipher_group); 21428c2ecf20Sopenharmony_ci for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) 21438c2ecf20Sopenharmony_ci pairwise |= 21448c2ecf20Sopenharmony_ci rndis_cipher_to_alg(sme->crypto.ciphers_pairwise[i]); 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (sme->crypto.n_ciphers_pairwise > 0 && 21478c2ecf20Sopenharmony_ci pairwise == RNDIS_WLAN_ALG_NONE) { 21488c2ecf20Sopenharmony_ci netdev_err(usbdev->net, "Unsupported pairwise cipher\n"); 21498c2ecf20Sopenharmony_ci return -ENOTSUPP; 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci for (i = 0; i < sme->crypto.n_akm_suites; i++) 21538c2ecf20Sopenharmony_ci keymgmt |= 21548c2ecf20Sopenharmony_ci rndis_akm_suite_to_key_mgmt(sme->crypto.akm_suites[i]); 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci if (sme->crypto.n_akm_suites > 0 && 21578c2ecf20Sopenharmony_ci keymgmt == RNDIS_WLAN_KEY_MGMT_NONE) { 21588c2ecf20Sopenharmony_ci netdev_err(usbdev->net, "Invalid keymgmt\n"); 21598c2ecf20Sopenharmony_ci return -ENOTSUPP; 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:0x%x]:0x%x)\n", 21638c2ecf20Sopenharmony_ci sme->ssid, sme->bssid, chan, 21648c2ecf20Sopenharmony_ci sme->privacy, sme->crypto.wpa_versions, sme->auth_type, 21658c2ecf20Sopenharmony_ci groupwise, pairwise, keymgmt); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci if (is_associated(usbdev)) 21688c2ecf20Sopenharmony_ci disassociate(usbdev, false); 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci ret = set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); 21718c2ecf20Sopenharmony_ci if (ret < 0) { 21728c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_infra_mode failed, %d\n", 21738c2ecf20Sopenharmony_ci ret); 21748c2ecf20Sopenharmony_ci goto err_turn_radio_on; 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci ret = set_auth_mode(usbdev, sme->crypto.wpa_versions, sme->auth_type, 21788c2ecf20Sopenharmony_ci keymgmt); 21798c2ecf20Sopenharmony_ci if (ret < 0) { 21808c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_auth_mode failed, %d\n", 21818c2ecf20Sopenharmony_ci ret); 21828c2ecf20Sopenharmony_ci goto err_turn_radio_on; 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci set_priv_filter(usbdev); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci ret = set_encr_mode(usbdev, pairwise, groupwise); 21888c2ecf20Sopenharmony_ci if (ret < 0) { 21898c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_encr_mode failed, %d\n", 21908c2ecf20Sopenharmony_ci ret); 21918c2ecf20Sopenharmony_ci goto err_turn_radio_on; 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (channel) { 21958c2ecf20Sopenharmony_ci ret = set_channel(usbdev, chan); 21968c2ecf20Sopenharmony_ci if (ret < 0) { 21978c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_channel failed, %d\n", 21988c2ecf20Sopenharmony_ci ret); 21998c2ecf20Sopenharmony_ci goto err_turn_radio_on; 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (sme->key && ((groupwise | pairwise) & RNDIS_WLAN_ALG_WEP)) { 22048c2ecf20Sopenharmony_ci priv->encr_tx_key_index = sme->key_idx; 22058c2ecf20Sopenharmony_ci ret = add_wep_key(usbdev, sme->key, sme->key_len, sme->key_idx); 22068c2ecf20Sopenharmony_ci if (ret < 0) { 22078c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: add_wep_key failed, %d (%d, %d)\n", 22088c2ecf20Sopenharmony_ci ret, sme->key_len, sme->key_idx); 22098c2ecf20Sopenharmony_ci goto err_turn_radio_on; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (sme->bssid && !is_zero_ether_addr(sme->bssid) && 22148c2ecf20Sopenharmony_ci !is_broadcast_ether_addr(sme->bssid)) { 22158c2ecf20Sopenharmony_ci ret = set_bssid(usbdev, sme->bssid); 22168c2ecf20Sopenharmony_ci if (ret < 0) { 22178c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_bssid failed, %d\n", 22188c2ecf20Sopenharmony_ci ret); 22198c2ecf20Sopenharmony_ci goto err_turn_radio_on; 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci } else 22228c2ecf20Sopenharmony_ci clear_bssid(usbdev); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci length = sme->ssid_len; 22258c2ecf20Sopenharmony_ci if (length > NDIS_802_11_LENGTH_SSID) 22268c2ecf20Sopenharmony_ci length = NDIS_802_11_LENGTH_SSID; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci memset(&ssid, 0, sizeof(ssid)); 22298c2ecf20Sopenharmony_ci ssid.length = cpu_to_le32(length); 22308c2ecf20Sopenharmony_ci memcpy(ssid.essid, sme->ssid, length); 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci /* Pause and purge rx queue, so we don't pass packets before 22338c2ecf20Sopenharmony_ci * 'media connect'-indication. 22348c2ecf20Sopenharmony_ci */ 22358c2ecf20Sopenharmony_ci usbnet_pause_rx(usbdev); 22368c2ecf20Sopenharmony_ci usbnet_purge_paused_rxq(usbdev); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci ret = set_essid(usbdev, &ssid); 22398c2ecf20Sopenharmony_ci if (ret < 0) 22408c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "connect: set_essid failed, %d\n", ret); 22418c2ecf20Sopenharmony_ci return ret; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_cierr_turn_radio_on: 22448c2ecf20Sopenharmony_ci disassociate(usbdev, true); 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci return ret; 22478c2ecf20Sopenharmony_ci} 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_cistatic int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, 22508c2ecf20Sopenharmony_ci u16 reason_code) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 22538c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cfg80211.disconnect(%d)\n", reason_code); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci priv->connected = false; 22588c2ecf20Sopenharmony_ci eth_zero_addr(priv->bssid); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci return deauthenticate(usbdev); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_cistatic int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, 22648c2ecf20Sopenharmony_ci struct cfg80211_ibss_params *params) 22658c2ecf20Sopenharmony_ci{ 22668c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 22678c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 22688c2ecf20Sopenharmony_ci struct ieee80211_channel *channel = params->chandef.chan; 22698c2ecf20Sopenharmony_ci struct ndis_80211_ssid ssid; 22708c2ecf20Sopenharmony_ci enum nl80211_auth_type auth_type; 22718c2ecf20Sopenharmony_ci int ret, alg, length, chan = -1; 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci if (channel) 22748c2ecf20Sopenharmony_ci chan = ieee80211_frequency_to_channel(channel->center_freq); 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci /* TODO: How to handle ad-hoc encryption? 22778c2ecf20Sopenharmony_ci * connect() has *key, join_ibss() doesn't. RNDIS requires key to be 22788c2ecf20Sopenharmony_ci * pre-shared for encryption (open/shared/wpa), is key set before 22798c2ecf20Sopenharmony_ci * join_ibss? Which auth_type to use (not in params)? What about WPA? 22808c2ecf20Sopenharmony_ci */ 22818c2ecf20Sopenharmony_ci if (params->privacy) { 22828c2ecf20Sopenharmony_ci auth_type = NL80211_AUTHTYPE_SHARED_KEY; 22838c2ecf20Sopenharmony_ci alg = RNDIS_WLAN_ALG_WEP; 22848c2ecf20Sopenharmony_ci } else { 22858c2ecf20Sopenharmony_ci auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; 22868c2ecf20Sopenharmony_ci alg = RNDIS_WLAN_ALG_NONE; 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cfg80211.join_ibss('%.32s':[%pM]:%d:%d)\n", 22908c2ecf20Sopenharmony_ci params->ssid, params->bssid, chan, params->privacy); 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (is_associated(usbdev)) 22938c2ecf20Sopenharmony_ci disassociate(usbdev, false); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci ret = set_infra_mode(usbdev, NDIS_80211_INFRA_ADHOC); 22968c2ecf20Sopenharmony_ci if (ret < 0) { 22978c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_infra_mode failed, %d\n", 22988c2ecf20Sopenharmony_ci ret); 22998c2ecf20Sopenharmony_ci goto err_turn_radio_on; 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci ret = set_auth_mode(usbdev, 0, auth_type, RNDIS_WLAN_KEY_MGMT_NONE); 23038c2ecf20Sopenharmony_ci if (ret < 0) { 23048c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_auth_mode failed, %d\n", 23058c2ecf20Sopenharmony_ci ret); 23068c2ecf20Sopenharmony_ci goto err_turn_radio_on; 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci set_priv_filter(usbdev); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci ret = set_encr_mode(usbdev, alg, RNDIS_WLAN_ALG_NONE); 23128c2ecf20Sopenharmony_ci if (ret < 0) { 23138c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_encr_mode failed, %d\n", 23148c2ecf20Sopenharmony_ci ret); 23158c2ecf20Sopenharmony_ci goto err_turn_radio_on; 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci if (channel) { 23198c2ecf20Sopenharmony_ci ret = set_channel(usbdev, chan); 23208c2ecf20Sopenharmony_ci if (ret < 0) { 23218c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_channel failed, %d\n", 23228c2ecf20Sopenharmony_ci ret); 23238c2ecf20Sopenharmony_ci goto err_turn_radio_on; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if (params->bssid && !is_zero_ether_addr(params->bssid) && 23288c2ecf20Sopenharmony_ci !is_broadcast_ether_addr(params->bssid)) { 23298c2ecf20Sopenharmony_ci ret = set_bssid(usbdev, params->bssid); 23308c2ecf20Sopenharmony_ci if (ret < 0) { 23318c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_bssid failed, %d\n", 23328c2ecf20Sopenharmony_ci ret); 23338c2ecf20Sopenharmony_ci goto err_turn_radio_on; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci } else 23368c2ecf20Sopenharmony_ci clear_bssid(usbdev); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci length = params->ssid_len; 23398c2ecf20Sopenharmony_ci if (length > NDIS_802_11_LENGTH_SSID) 23408c2ecf20Sopenharmony_ci length = NDIS_802_11_LENGTH_SSID; 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci memset(&ssid, 0, sizeof(ssid)); 23438c2ecf20Sopenharmony_ci ssid.length = cpu_to_le32(length); 23448c2ecf20Sopenharmony_ci memcpy(ssid.essid, params->ssid, length); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci /* Don't need to pause rx queue for ad-hoc. */ 23478c2ecf20Sopenharmony_ci usbnet_purge_paused_rxq(usbdev); 23488c2ecf20Sopenharmony_ci usbnet_resume_rx(usbdev); 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci ret = set_essid(usbdev, &ssid); 23518c2ecf20Sopenharmony_ci if (ret < 0) 23528c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "join_ibss: set_essid failed, %d\n", 23538c2ecf20Sopenharmony_ci ret); 23548c2ecf20Sopenharmony_ci return ret; 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_cierr_turn_radio_on: 23578c2ecf20Sopenharmony_ci disassociate(usbdev, true); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci return ret; 23608c2ecf20Sopenharmony_ci} 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_cistatic int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) 23638c2ecf20Sopenharmony_ci{ 23648c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 23658c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cfg80211.leave_ibss()\n"); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci priv->connected = false; 23708c2ecf20Sopenharmony_ci eth_zero_addr(priv->bssid); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci return deauthenticate(usbdev); 23738c2ecf20Sopenharmony_ci} 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_cistatic int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, 23768c2ecf20Sopenharmony_ci u8 key_index, bool pairwise, const u8 *mac_addr, 23778c2ecf20Sopenharmony_ci struct key_params *params) 23788c2ecf20Sopenharmony_ci{ 23798c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 23808c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 23818c2ecf20Sopenharmony_ci __le32 flags; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%i, %pM, %08x)\n", 23848c2ecf20Sopenharmony_ci __func__, key_index, mac_addr, params->cipher); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci switch (params->cipher) { 23878c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 23888c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 23898c2ecf20Sopenharmony_ci return add_wep_key(usbdev, params->key, params->key_len, 23908c2ecf20Sopenharmony_ci key_index); 23918c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 23928c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 23938c2ecf20Sopenharmony_ci flags = 0; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (params->seq && params->seq_len > 0) 23968c2ecf20Sopenharmony_ci flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; 23978c2ecf20Sopenharmony_ci if (mac_addr) 23988c2ecf20Sopenharmony_ci flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY | 23998c2ecf20Sopenharmony_ci NDIS_80211_ADDKEY_TRANSMIT_KEY; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci return add_wpa_key(usbdev, params->key, params->key_len, 24028c2ecf20Sopenharmony_ci key_index, mac_addr, params->seq, 24038c2ecf20Sopenharmony_ci params->seq_len, params->cipher, flags); 24048c2ecf20Sopenharmony_ci default: 24058c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): unsupported cipher %08x\n", 24068c2ecf20Sopenharmony_ci __func__, params->cipher); 24078c2ecf20Sopenharmony_ci return -ENOTSUPP; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci} 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_cistatic int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, 24128c2ecf20Sopenharmony_ci u8 key_index, bool pairwise, const u8 *mac_addr) 24138c2ecf20Sopenharmony_ci{ 24148c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 24158c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%i, %pM)\n", __func__, key_index, mac_addr); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci return remove_key(usbdev, key_index, mac_addr); 24208c2ecf20Sopenharmony_ci} 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_cistatic int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, 24238c2ecf20Sopenharmony_ci u8 key_index, bool unicast, bool multicast) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 24268c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 24278c2ecf20Sopenharmony_ci struct rndis_wlan_encr_key key; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (key_index >= RNDIS_WLAN_NUM_KEYS) 24328c2ecf20Sopenharmony_ci return -ENOENT; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci priv->encr_tx_key_index = key_index; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (is_wpa_key(priv, key_index)) 24378c2ecf20Sopenharmony_ci return 0; 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci key = priv->encr_keys[key_index]; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci return add_wep_key(usbdev, key.material, key.len, key_index); 24428c2ecf20Sopenharmony_ci} 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistatic void rndis_fill_station_info(struct usbnet *usbdev, 24458c2ecf20Sopenharmony_ci struct station_info *sinfo) 24468c2ecf20Sopenharmony_ci{ 24478c2ecf20Sopenharmony_ci __le32 linkspeed, rssi; 24488c2ecf20Sopenharmony_ci int ret, len; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci memset(sinfo, 0, sizeof(*sinfo)); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci len = sizeof(linkspeed); 24538c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); 24548c2ecf20Sopenharmony_ci if (ret == 0) { 24558c2ecf20Sopenharmony_ci sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; 24568c2ecf20Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci len = sizeof(rssi); 24608c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, 24618c2ecf20Sopenharmony_ci &rssi, &len); 24628c2ecf20Sopenharmony_ci if (ret == 0) { 24638c2ecf20Sopenharmony_ci sinfo->signal = level_to_qual(le32_to_cpu(rssi)); 24648c2ecf20Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 24658c2ecf20Sopenharmony_ci } 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_cistatic int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, 24698c2ecf20Sopenharmony_ci const u8 *mac, struct station_info *sinfo) 24708c2ecf20Sopenharmony_ci{ 24718c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 24728c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci if (!ether_addr_equal(priv->bssid, mac)) 24758c2ecf20Sopenharmony_ci return -ENOENT; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci rndis_fill_station_info(usbdev, sinfo); 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci return 0; 24808c2ecf20Sopenharmony_ci} 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_cistatic int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, 24838c2ecf20Sopenharmony_ci int idx, u8 *mac, struct station_info *sinfo) 24848c2ecf20Sopenharmony_ci{ 24858c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 24868c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci if (idx != 0) 24898c2ecf20Sopenharmony_ci return -ENOENT; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci memcpy(mac, priv->bssid, ETH_ALEN); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci rndis_fill_station_info(usbdev, sinfo); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci return 0; 24968c2ecf20Sopenharmony_ci} 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_cistatic int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, 24998c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa) 25008c2ecf20Sopenharmony_ci{ 25018c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 25028c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 25038c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids; 25048c2ecf20Sopenharmony_ci u32 *tmp = (u32 *)pmksa->pmkid; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, 25078c2ecf20Sopenharmony_ci pmksa->bssid, 25088c2ecf20Sopenharmony_ci cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), 25098c2ecf20Sopenharmony_ci cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci pmkids = get_device_pmkids(usbdev); 25128c2ecf20Sopenharmony_ci if (IS_ERR(pmkids)) { 25138c2ecf20Sopenharmony_ci /* couldn't read PMKID cache from device */ 25148c2ecf20Sopenharmony_ci return PTR_ERR(pmkids); 25158c2ecf20Sopenharmony_ci } 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci pmkids = update_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); 25188c2ecf20Sopenharmony_ci if (IS_ERR(pmkids)) { 25198c2ecf20Sopenharmony_ci /* not found, list full, etc */ 25208c2ecf20Sopenharmony_ci return PTR_ERR(pmkids); 25218c2ecf20Sopenharmony_ci } 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci return set_device_pmkids(usbdev, pmkids); 25248c2ecf20Sopenharmony_ci} 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_cistatic int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, 25278c2ecf20Sopenharmony_ci struct cfg80211_pmksa *pmksa) 25288c2ecf20Sopenharmony_ci{ 25298c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 25308c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 25318c2ecf20Sopenharmony_ci struct ndis_80211_pmkid *pmkids; 25328c2ecf20Sopenharmony_ci u32 *tmp = (u32 *)pmksa->pmkid; 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, 25358c2ecf20Sopenharmony_ci pmksa->bssid, 25368c2ecf20Sopenharmony_ci cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), 25378c2ecf20Sopenharmony_ci cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci pmkids = get_device_pmkids(usbdev); 25408c2ecf20Sopenharmony_ci if (IS_ERR(pmkids)) { 25418c2ecf20Sopenharmony_ci /* Couldn't read PMKID cache from device */ 25428c2ecf20Sopenharmony_ci return PTR_ERR(pmkids); 25438c2ecf20Sopenharmony_ci } 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci pmkids = remove_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); 25468c2ecf20Sopenharmony_ci if (IS_ERR(pmkids)) { 25478c2ecf20Sopenharmony_ci /* not found, etc */ 25488c2ecf20Sopenharmony_ci return PTR_ERR(pmkids); 25498c2ecf20Sopenharmony_ci } 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci return set_device_pmkids(usbdev, pmkids); 25528c2ecf20Sopenharmony_ci} 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_cistatic int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) 25558c2ecf20Sopenharmony_ci{ 25568c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 25578c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 25588c2ecf20Sopenharmony_ci struct ndis_80211_pmkid pmkid; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s()\n", __func__); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci memset(&pmkid, 0, sizeof(pmkid)); 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci pmkid.length = cpu_to_le32(sizeof(pmkid)); 25658c2ecf20Sopenharmony_ci pmkid.bssid_info_count = cpu_to_le32(0); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci return rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, 25688c2ecf20Sopenharmony_ci &pmkid, sizeof(pmkid)); 25698c2ecf20Sopenharmony_ci} 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_cistatic int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 25728c2ecf20Sopenharmony_ci bool enabled, int timeout) 25738c2ecf20Sopenharmony_ci{ 25748c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 25758c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 25768c2ecf20Sopenharmony_ci int power_mode; 25778c2ecf20Sopenharmony_ci __le32 mode; 25788c2ecf20Sopenharmony_ci int ret; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci if (priv->device_type != RNDIS_BCM4320B) 25818c2ecf20Sopenharmony_ci return -ENOTSUPP; 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, 25848c2ecf20Sopenharmony_ci enabled ? "enabled" : "disabled", 25858c2ecf20Sopenharmony_ci timeout); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci if (enabled) 25888c2ecf20Sopenharmony_ci power_mode = NDIS_80211_POWER_MODE_FAST_PSP; 25898c2ecf20Sopenharmony_ci else 25908c2ecf20Sopenharmony_ci power_mode = NDIS_80211_POWER_MODE_CAM; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci if (power_mode == priv->power_mode) 25938c2ecf20Sopenharmony_ci return 0; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci priv->power_mode = power_mode; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci mode = cpu_to_le32(power_mode); 25988c2ecf20Sopenharmony_ci ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_POWER_MODE, 25998c2ecf20Sopenharmony_ci &mode, sizeof(mode)); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_POWER_MODE -> %d\n", 26028c2ecf20Sopenharmony_ci __func__, ret); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci return ret; 26058c2ecf20Sopenharmony_ci} 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_cistatic int rndis_set_cqm_rssi_config(struct wiphy *wiphy, 26088c2ecf20Sopenharmony_ci struct net_device *dev, 26098c2ecf20Sopenharmony_ci s32 rssi_thold, u32 rssi_hyst) 26108c2ecf20Sopenharmony_ci{ 26118c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = wiphy_priv(wiphy); 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci priv->cqm_rssi_thold = rssi_thold; 26148c2ecf20Sopenharmony_ci priv->cqm_rssi_hyst = rssi_hyst; 26158c2ecf20Sopenharmony_ci priv->last_cqm_event_rssi = 0; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci return 0; 26188c2ecf20Sopenharmony_ci} 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_cistatic void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, 26218c2ecf20Sopenharmony_ci struct ndis_80211_assoc_info *info) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 26248c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 26258c2ecf20Sopenharmony_ci struct ndis_80211_ssid ssid; 26268c2ecf20Sopenharmony_ci struct cfg80211_bss *bss; 26278c2ecf20Sopenharmony_ci s32 signal; 26288c2ecf20Sopenharmony_ci u64 timestamp; 26298c2ecf20Sopenharmony_ci u16 capability; 26308c2ecf20Sopenharmony_ci u32 beacon_period = 0; 26318c2ecf20Sopenharmony_ci __le32 rssi; 26328c2ecf20Sopenharmony_ci u8 ie_buf[34]; 26338c2ecf20Sopenharmony_ci int len, ret, ie_len; 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci /* Get signal quality, in case of error use rssi=0 and ignore error. */ 26368c2ecf20Sopenharmony_ci len = sizeof(rssi); 26378c2ecf20Sopenharmony_ci rssi = 0; 26388c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, 26398c2ecf20Sopenharmony_ci &rssi, &len); 26408c2ecf20Sopenharmony_ci signal = level_to_qual(le32_to_cpu(rssi)); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_RSSI -> %d, " 26438c2ecf20Sopenharmony_ci "rssi:%d, qual: %d\n", __func__, ret, le32_to_cpu(rssi), 26448c2ecf20Sopenharmony_ci level_to_qual(le32_to_cpu(rssi))); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci /* Get AP capabilities */ 26478c2ecf20Sopenharmony_ci if (info) { 26488c2ecf20Sopenharmony_ci capability = le16_to_cpu(info->resp_ie.capa); 26498c2ecf20Sopenharmony_ci } else { 26508c2ecf20Sopenharmony_ci /* Set atleast ESS/IBSS capability */ 26518c2ecf20Sopenharmony_ci capability = (priv->infra_mode == NDIS_80211_INFRA_INFRA) ? 26528c2ecf20Sopenharmony_ci WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS; 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci /* Get channel and beacon interval */ 26568c2ecf20Sopenharmony_ci channel = get_current_channel(usbdev, &beacon_period); 26578c2ecf20Sopenharmony_ci if (!channel) { 26588c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "%s(): could not get channel.\n", 26598c2ecf20Sopenharmony_ci __func__); 26608c2ecf20Sopenharmony_ci return; 26618c2ecf20Sopenharmony_ci } 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci /* Get SSID, in case of error, use zero length SSID and ignore error. */ 26648c2ecf20Sopenharmony_ci len = sizeof(ssid); 26658c2ecf20Sopenharmony_ci memset(&ssid, 0, sizeof(ssid)); 26668c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_SSID, 26678c2ecf20Sopenharmony_ci &ssid, &len); 26688c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_SSID -> %d, len: %d, ssid: " 26698c2ecf20Sopenharmony_ci "'%.32s'\n", __func__, ret, 26708c2ecf20Sopenharmony_ci le32_to_cpu(ssid.length), ssid.essid); 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci if (le32_to_cpu(ssid.length) > 32) 26738c2ecf20Sopenharmony_ci ssid.length = cpu_to_le32(32); 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci ie_buf[0] = WLAN_EID_SSID; 26768c2ecf20Sopenharmony_ci ie_buf[1] = le32_to_cpu(ssid.length); 26778c2ecf20Sopenharmony_ci memcpy(&ie_buf[2], ssid.essid, le32_to_cpu(ssid.length)); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci ie_len = le32_to_cpu(ssid.length) + 2; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci /* no tsf */ 26828c2ecf20Sopenharmony_ci timestamp = 0; 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, " 26858c2ecf20Sopenharmony_ci "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), " 26868c2ecf20Sopenharmony_ci "signal:%d\n", __func__, (channel ? channel->center_freq : -1), 26878c2ecf20Sopenharmony_ci bssid, (u32)timestamp, capability, beacon_period, ie_len, 26888c2ecf20Sopenharmony_ci ssid.essid, signal); 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, 26918c2ecf20Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, bssid, 26928c2ecf20Sopenharmony_ci timestamp, capability, beacon_period, 26938c2ecf20Sopenharmony_ci ie_buf, ie_len, signal, GFP_KERNEL); 26948c2ecf20Sopenharmony_ci cfg80211_put_bss(priv->wdev.wiphy, bss); 26958c2ecf20Sopenharmony_ci} 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci/* 26988c2ecf20Sopenharmony_ci * workers, indication handlers, device poller 26998c2ecf20Sopenharmony_ci */ 27008c2ecf20Sopenharmony_cistatic void rndis_wlan_do_link_up_work(struct usbnet *usbdev) 27018c2ecf20Sopenharmony_ci{ 27028c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 27038c2ecf20Sopenharmony_ci struct ndis_80211_assoc_info *info = NULL; 27048c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 27058c2ecf20Sopenharmony_ci unsigned int resp_ie_len, req_ie_len; 27068c2ecf20Sopenharmony_ci unsigned int offset; 27078c2ecf20Sopenharmony_ci u8 *req_ie, *resp_ie; 27088c2ecf20Sopenharmony_ci int ret; 27098c2ecf20Sopenharmony_ci bool roamed = false; 27108c2ecf20Sopenharmony_ci bool match_bss; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { 27138c2ecf20Sopenharmony_ci /* received media connect indication while connected, either 27148c2ecf20Sopenharmony_ci * device reassociated with same AP or roamed to new. */ 27158c2ecf20Sopenharmony_ci roamed = true; 27168c2ecf20Sopenharmony_ci } 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci req_ie_len = 0; 27198c2ecf20Sopenharmony_ci resp_ie_len = 0; 27208c2ecf20Sopenharmony_ci req_ie = NULL; 27218c2ecf20Sopenharmony_ci resp_ie = NULL; 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { 27248c2ecf20Sopenharmony_ci info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 27258c2ecf20Sopenharmony_ci if (!info) { 27268c2ecf20Sopenharmony_ci /* No memory? Try resume work later */ 27278c2ecf20Sopenharmony_ci set_bit(WORK_LINK_UP, &priv->work_pending); 27288c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->work); 27298c2ecf20Sopenharmony_ci return; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci /* Get association info IEs from device. */ 27338c2ecf20Sopenharmony_ci ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE); 27348c2ecf20Sopenharmony_ci if (!ret) { 27358c2ecf20Sopenharmony_ci req_ie_len = le32_to_cpu(info->req_ie_length); 27368c2ecf20Sopenharmony_ci if (req_ie_len > CONTROL_BUFFER_SIZE) 27378c2ecf20Sopenharmony_ci req_ie_len = CONTROL_BUFFER_SIZE; 27388c2ecf20Sopenharmony_ci if (req_ie_len != 0) { 27398c2ecf20Sopenharmony_ci offset = le32_to_cpu(info->offset_req_ies); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci if (offset > CONTROL_BUFFER_SIZE) 27428c2ecf20Sopenharmony_ci offset = CONTROL_BUFFER_SIZE; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci req_ie = (u8 *)info + offset; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci if (offset + req_ie_len > CONTROL_BUFFER_SIZE) 27478c2ecf20Sopenharmony_ci req_ie_len = 27488c2ecf20Sopenharmony_ci CONTROL_BUFFER_SIZE - offset; 27498c2ecf20Sopenharmony_ci } 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci resp_ie_len = le32_to_cpu(info->resp_ie_length); 27528c2ecf20Sopenharmony_ci if (resp_ie_len > CONTROL_BUFFER_SIZE) 27538c2ecf20Sopenharmony_ci resp_ie_len = CONTROL_BUFFER_SIZE; 27548c2ecf20Sopenharmony_ci if (resp_ie_len != 0) { 27558c2ecf20Sopenharmony_ci offset = le32_to_cpu(info->offset_resp_ies); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci if (offset > CONTROL_BUFFER_SIZE) 27588c2ecf20Sopenharmony_ci offset = CONTROL_BUFFER_SIZE; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci resp_ie = (u8 *)info + offset; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (offset + resp_ie_len > CONTROL_BUFFER_SIZE) 27638c2ecf20Sopenharmony_ci resp_ie_len = 27648c2ecf20Sopenharmony_ci CONTROL_BUFFER_SIZE - offset; 27658c2ecf20Sopenharmony_ci } 27668c2ecf20Sopenharmony_ci } else { 27678c2ecf20Sopenharmony_ci /* Since rndis_wlan_craft_connected_bss() might use info 27688c2ecf20Sopenharmony_ci * later and expects info to contain valid data if 27698c2ecf20Sopenharmony_ci * non-null, free info and set NULL here. 27708c2ecf20Sopenharmony_ci */ 27718c2ecf20Sopenharmony_ci kfree(info); 27728c2ecf20Sopenharmony_ci info = NULL; 27738c2ecf20Sopenharmony_ci } 27748c2ecf20Sopenharmony_ci } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) 27758c2ecf20Sopenharmony_ci return; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci ret = get_bssid(usbdev, bssid); 27788c2ecf20Sopenharmony_ci if (ret < 0) 27798c2ecf20Sopenharmony_ci memset(bssid, 0, sizeof(bssid)); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "link up work: [%pM]%s\n", 27828c2ecf20Sopenharmony_ci bssid, roamed ? " roamed" : ""); 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci /* Internal bss list in device should contain at least the currently 27858c2ecf20Sopenharmony_ci * connected bss and we can get it to cfg80211 with 27868c2ecf20Sopenharmony_ci * rndis_check_bssid_list(). 27878c2ecf20Sopenharmony_ci * 27888c2ecf20Sopenharmony_ci * NDIS spec says: "If the device is associated, but the associated 27898c2ecf20Sopenharmony_ci * BSSID is not in its BSSID scan list, then the driver must add an 27908c2ecf20Sopenharmony_ci * entry for the BSSID at the end of the data that it returns in 27918c2ecf20Sopenharmony_ci * response to query of RNDIS_OID_802_11_BSSID_LIST." 27928c2ecf20Sopenharmony_ci * 27938c2ecf20Sopenharmony_ci * NOTE: Seems to be true for BCM4320b variant, but not BCM4320a. 27948c2ecf20Sopenharmony_ci */ 27958c2ecf20Sopenharmony_ci match_bss = false; 27968c2ecf20Sopenharmony_ci rndis_check_bssid_list(usbdev, bssid, &match_bss); 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(bssid) && !match_bss) { 27998c2ecf20Sopenharmony_ci /* Couldn't get bss from device, we need to manually craft bss 28008c2ecf20Sopenharmony_ci * for cfg80211. 28018c2ecf20Sopenharmony_ci */ 28028c2ecf20Sopenharmony_ci rndis_wlan_craft_connected_bss(usbdev, bssid, info); 28038c2ecf20Sopenharmony_ci } 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { 28068c2ecf20Sopenharmony_ci if (!roamed) { 28078c2ecf20Sopenharmony_ci cfg80211_connect_result(usbdev->net, bssid, req_ie, 28088c2ecf20Sopenharmony_ci req_ie_len, resp_ie, 28098c2ecf20Sopenharmony_ci resp_ie_len, 0, GFP_KERNEL); 28108c2ecf20Sopenharmony_ci } else { 28118c2ecf20Sopenharmony_ci struct cfg80211_roam_info roam_info = { 28128c2ecf20Sopenharmony_ci .channel = get_current_channel(usbdev, NULL), 28138c2ecf20Sopenharmony_ci .bssid = bssid, 28148c2ecf20Sopenharmony_ci .req_ie = req_ie, 28158c2ecf20Sopenharmony_ci .req_ie_len = req_ie_len, 28168c2ecf20Sopenharmony_ci .resp_ie = resp_ie, 28178c2ecf20Sopenharmony_ci .resp_ie_len = resp_ie_len, 28188c2ecf20Sopenharmony_ci }; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci cfg80211_roamed(usbdev->net, &roam_info, GFP_KERNEL); 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) 28238c2ecf20Sopenharmony_ci cfg80211_ibss_joined(usbdev->net, bssid, 28248c2ecf20Sopenharmony_ci get_current_channel(usbdev, NULL), 28258c2ecf20Sopenharmony_ci GFP_KERNEL); 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci kfree(info); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci priv->connected = true; 28308c2ecf20Sopenharmony_ci memcpy(priv->bssid, bssid, ETH_ALEN); 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci usbnet_resume_rx(usbdev); 28338c2ecf20Sopenharmony_ci netif_carrier_on(usbdev->net); 28348c2ecf20Sopenharmony_ci} 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_cistatic void rndis_wlan_do_link_down_work(struct usbnet *usbdev) 28378c2ecf20Sopenharmony_ci{ 28388c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci if (priv->connected) { 28418c2ecf20Sopenharmony_ci priv->connected = false; 28428c2ecf20Sopenharmony_ci eth_zero_addr(priv->bssid); 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci deauthenticate(usbdev); 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci cfg80211_disconnected(usbdev->net, 0, NULL, 0, true, GFP_KERNEL); 28478c2ecf20Sopenharmony_ci } 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci netif_carrier_off(usbdev->net); 28508c2ecf20Sopenharmony_ci} 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_cistatic void rndis_wlan_worker(struct work_struct *work) 28538c2ecf20Sopenharmony_ci{ 28548c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = 28558c2ecf20Sopenharmony_ci container_of(work, struct rndis_wlan_private, work); 28568c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) 28598c2ecf20Sopenharmony_ci rndis_wlan_do_link_up_work(usbdev); 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) 28628c2ecf20Sopenharmony_ci rndis_wlan_do_link_down_work(usbdev); 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) 28658c2ecf20Sopenharmony_ci set_multicast_list(usbdev); 28668c2ecf20Sopenharmony_ci} 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_cistatic void rndis_wlan_set_multicast_list(struct net_device *dev) 28698c2ecf20Sopenharmony_ci{ 28708c2ecf20Sopenharmony_ci struct usbnet *usbdev = netdev_priv(dev); 28718c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) 28748c2ecf20Sopenharmony_ci return; 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); 28778c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->work); 28788c2ecf20Sopenharmony_ci} 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_cistatic void rndis_wlan_auth_indication(struct usbnet *usbdev, 28818c2ecf20Sopenharmony_ci struct ndis_80211_status_indication *indication, 28828c2ecf20Sopenharmony_ci int len) 28838c2ecf20Sopenharmony_ci{ 28848c2ecf20Sopenharmony_ci u8 *buf; 28858c2ecf20Sopenharmony_ci const char *type; 28868c2ecf20Sopenharmony_ci int flags, buflen, key_id; 28878c2ecf20Sopenharmony_ci bool pairwise_error, group_error; 28888c2ecf20Sopenharmony_ci struct ndis_80211_auth_request *auth_req; 28898c2ecf20Sopenharmony_ci enum nl80211_key_type key_type; 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci /* must have at least one array entry */ 28928c2ecf20Sopenharmony_ci if (len < offsetof(struct ndis_80211_status_indication, u) + 28938c2ecf20Sopenharmony_ci sizeof(struct ndis_80211_auth_request)) { 28948c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "authentication indication: too short message (%i)\n", 28958c2ecf20Sopenharmony_ci len); 28968c2ecf20Sopenharmony_ci return; 28978c2ecf20Sopenharmony_ci } 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci buf = (void *)&indication->u.auth_request[0]; 29008c2ecf20Sopenharmony_ci buflen = len - offsetof(struct ndis_80211_status_indication, u); 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci while (buflen >= sizeof(*auth_req)) { 29038c2ecf20Sopenharmony_ci auth_req = (void *)buf; 29048c2ecf20Sopenharmony_ci if (buflen < le32_to_cpu(auth_req->length)) 29058c2ecf20Sopenharmony_ci return; 29068c2ecf20Sopenharmony_ci type = "unknown"; 29078c2ecf20Sopenharmony_ci flags = le32_to_cpu(auth_req->flags); 29088c2ecf20Sopenharmony_ci pairwise_error = false; 29098c2ecf20Sopenharmony_ci group_error = false; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci if (flags & 0x1) 29128c2ecf20Sopenharmony_ci type = "reauth request"; 29138c2ecf20Sopenharmony_ci if (flags & 0x2) 29148c2ecf20Sopenharmony_ci type = "key update request"; 29158c2ecf20Sopenharmony_ci if (flags & 0x6) { 29168c2ecf20Sopenharmony_ci pairwise_error = true; 29178c2ecf20Sopenharmony_ci type = "pairwise_error"; 29188c2ecf20Sopenharmony_ci } 29198c2ecf20Sopenharmony_ci if (flags & 0xe) { 29208c2ecf20Sopenharmony_ci group_error = true; 29218c2ecf20Sopenharmony_ci type = "group_error"; 29228c2ecf20Sopenharmony_ci } 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "authentication indication: %s (0x%08x)\n", 29258c2ecf20Sopenharmony_ci type, le32_to_cpu(auth_req->flags)); 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci if (pairwise_error) { 29288c2ecf20Sopenharmony_ci key_type = NL80211_KEYTYPE_PAIRWISE; 29298c2ecf20Sopenharmony_ci key_id = -1; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci cfg80211_michael_mic_failure(usbdev->net, 29328c2ecf20Sopenharmony_ci auth_req->bssid, 29338c2ecf20Sopenharmony_ci key_type, key_id, NULL, 29348c2ecf20Sopenharmony_ci GFP_KERNEL); 29358c2ecf20Sopenharmony_ci } 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci if (group_error) { 29388c2ecf20Sopenharmony_ci key_type = NL80211_KEYTYPE_GROUP; 29398c2ecf20Sopenharmony_ci key_id = -1; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci cfg80211_michael_mic_failure(usbdev->net, 29428c2ecf20Sopenharmony_ci auth_req->bssid, 29438c2ecf20Sopenharmony_ci key_type, key_id, NULL, 29448c2ecf20Sopenharmony_ci GFP_KERNEL); 29458c2ecf20Sopenharmony_ci } 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci buflen -= le32_to_cpu(auth_req->length); 29488c2ecf20Sopenharmony_ci buf += le32_to_cpu(auth_req->length); 29498c2ecf20Sopenharmony_ci } 29508c2ecf20Sopenharmony_ci} 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_cistatic void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev, 29538c2ecf20Sopenharmony_ci struct ndis_80211_status_indication *indication, 29548c2ecf20Sopenharmony_ci int len) 29558c2ecf20Sopenharmony_ci{ 29568c2ecf20Sopenharmony_ci struct ndis_80211_pmkid_cand_list *cand_list; 29578c2ecf20Sopenharmony_ci int list_len, expected_len, i; 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci if (len < offsetof(struct ndis_80211_status_indication, u) + 29608c2ecf20Sopenharmony_ci sizeof(struct ndis_80211_pmkid_cand_list)) { 29618c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "pmkid candidate list indication: too short message (%i)\n", 29628c2ecf20Sopenharmony_ci len); 29638c2ecf20Sopenharmony_ci return; 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci list_len = le32_to_cpu(indication->u.cand_list.num_candidates) * 29678c2ecf20Sopenharmony_ci sizeof(struct ndis_80211_pmkid_candidate); 29688c2ecf20Sopenharmony_ci expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len + 29698c2ecf20Sopenharmony_ci offsetof(struct ndis_80211_status_indication, u); 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci if (len < expected_len) { 29728c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "pmkid candidate list indication: list larger than buffer (%i < %i)\n", 29738c2ecf20Sopenharmony_ci len, expected_len); 29748c2ecf20Sopenharmony_ci return; 29758c2ecf20Sopenharmony_ci } 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci cand_list = &indication->u.cand_list; 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "pmkid candidate list indication: version %i, candidates %i\n", 29808c2ecf20Sopenharmony_ci le32_to_cpu(cand_list->version), 29818c2ecf20Sopenharmony_ci le32_to_cpu(cand_list->num_candidates)); 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci if (le32_to_cpu(cand_list->version) != 1) 29848c2ecf20Sopenharmony_ci return; 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) { 29878c2ecf20Sopenharmony_ci struct ndis_80211_pmkid_candidate *cand = 29888c2ecf20Sopenharmony_ci &cand_list->candidate_list[i]; 29898c2ecf20Sopenharmony_ci bool preauth = !!(cand->flags & NDIS_80211_PMKID_CAND_PREAUTH); 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, preauth: %d, bssid: %pM\n", 29928c2ecf20Sopenharmony_ci i, le32_to_cpu(cand->flags), preauth, cand->bssid); 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci cfg80211_pmksa_candidate_notify(usbdev->net, i, cand->bssid, 29958c2ecf20Sopenharmony_ci preauth, GFP_ATOMIC); 29968c2ecf20Sopenharmony_ci } 29978c2ecf20Sopenharmony_ci} 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_cistatic void rndis_wlan_media_specific_indication(struct usbnet *usbdev, 30008c2ecf20Sopenharmony_ci struct rndis_indicate *msg, int buflen) 30018c2ecf20Sopenharmony_ci{ 30028c2ecf20Sopenharmony_ci struct ndis_80211_status_indication *indication; 30038c2ecf20Sopenharmony_ci unsigned int len, offset; 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci offset = offsetof(struct rndis_indicate, status) + 30068c2ecf20Sopenharmony_ci le32_to_cpu(msg->offset); 30078c2ecf20Sopenharmony_ci len = le32_to_cpu(msg->length); 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci if (len < 8) { 30108c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media specific indication, ignore too short message (%i < 8)\n", 30118c2ecf20Sopenharmony_ci len); 30128c2ecf20Sopenharmony_ci return; 30138c2ecf20Sopenharmony_ci } 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci if (len > buflen || offset > buflen || offset + len > buflen) { 30168c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media specific indication, too large to fit to buffer (%i > %i)\n", 30178c2ecf20Sopenharmony_ci offset + len, buflen); 30188c2ecf20Sopenharmony_ci return; 30198c2ecf20Sopenharmony_ci } 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci indication = (void *)((u8 *)msg + offset); 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci switch (le32_to_cpu(indication->status_type)) { 30248c2ecf20Sopenharmony_ci case NDIS_80211_STATUSTYPE_RADIOSTATE: 30258c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "radio state indication: %i\n", 30268c2ecf20Sopenharmony_ci le32_to_cpu(indication->u.radio_status)); 30278c2ecf20Sopenharmony_ci return; 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE: 30308c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media stream mode indication: %i\n", 30318c2ecf20Sopenharmony_ci le32_to_cpu(indication->u.media_stream_mode)); 30328c2ecf20Sopenharmony_ci return; 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci case NDIS_80211_STATUSTYPE_AUTHENTICATION: 30358c2ecf20Sopenharmony_ci rndis_wlan_auth_indication(usbdev, indication, len); 30368c2ecf20Sopenharmony_ci return; 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_ci case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST: 30398c2ecf20Sopenharmony_ci rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len); 30408c2ecf20Sopenharmony_ci return; 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci default: 30438c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media specific indication: unknown status type 0x%08x\n", 30448c2ecf20Sopenharmony_ci le32_to_cpu(indication->status_type)); 30458c2ecf20Sopenharmony_ci } 30468c2ecf20Sopenharmony_ci} 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_cistatic void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) 30498c2ecf20Sopenharmony_ci{ 30508c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 30518c2ecf20Sopenharmony_ci struct rndis_indicate *msg = ind; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci switch (le32_to_cpu(msg->status)) { 30548c2ecf20Sopenharmony_ci case RNDIS_STATUS_MEDIA_CONNECT: 30558c2ecf20Sopenharmony_ci if (priv->current_command_oid == RNDIS_OID_802_11_ADD_KEY) { 30568c2ecf20Sopenharmony_ci /* RNDIS_OID_802_11_ADD_KEY causes sometimes extra 30578c2ecf20Sopenharmony_ci * "media connect" indications which confuses driver 30588c2ecf20Sopenharmony_ci * and userspace to think that device is 30598c2ecf20Sopenharmony_ci * roaming/reassociating when it isn't. 30608c2ecf20Sopenharmony_ci */ 30618c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "ignored RNDIS_OID_802_11_ADD_KEY triggered 'media connect'\n"); 30628c2ecf20Sopenharmony_ci return; 30638c2ecf20Sopenharmony_ci } 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci usbnet_pause_rx(usbdev); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media connect\n"); 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci /* queue work to avoid recursive calls into rndis_command */ 30708c2ecf20Sopenharmony_ci set_bit(WORK_LINK_UP, &priv->work_pending); 30718c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->work); 30728c2ecf20Sopenharmony_ci break; 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ci case RNDIS_STATUS_MEDIA_DISCONNECT: 30758c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "media disconnect\n"); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci /* queue work to avoid recursive calls into rndis_command */ 30788c2ecf20Sopenharmony_ci set_bit(WORK_LINK_DOWN, &priv->work_pending); 30798c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->work); 30808c2ecf20Sopenharmony_ci break; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION: 30838c2ecf20Sopenharmony_ci rndis_wlan_media_specific_indication(usbdev, msg, buflen); 30848c2ecf20Sopenharmony_ci break; 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci default: 30878c2ecf20Sopenharmony_ci netdev_info(usbdev->net, "indication: 0x%08x\n", 30888c2ecf20Sopenharmony_ci le32_to_cpu(msg->status)); 30898c2ecf20Sopenharmony_ci break; 30908c2ecf20Sopenharmony_ci } 30918c2ecf20Sopenharmony_ci} 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_cistatic int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) 30948c2ecf20Sopenharmony_ci{ 30958c2ecf20Sopenharmony_ci struct { 30968c2ecf20Sopenharmony_ci __le32 num_items; 30978c2ecf20Sopenharmony_ci __le32 items[8]; 30988c2ecf20Sopenharmony_ci } networks_supported; 30998c2ecf20Sopenharmony_ci struct ndis_80211_capability caps; 31008c2ecf20Sopenharmony_ci int len, retval, i, n; 31018c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci /* determine supported modes */ 31048c2ecf20Sopenharmony_ci len = sizeof(networks_supported); 31058c2ecf20Sopenharmony_ci retval = rndis_query_oid(usbdev, 31068c2ecf20Sopenharmony_ci RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED, 31078c2ecf20Sopenharmony_ci &networks_supported, &len); 31088c2ecf20Sopenharmony_ci if (!retval) { 31098c2ecf20Sopenharmony_ci n = le32_to_cpu(networks_supported.num_items); 31108c2ecf20Sopenharmony_ci if (n > 8) 31118c2ecf20Sopenharmony_ci n = 8; 31128c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 31138c2ecf20Sopenharmony_ci switch (le32_to_cpu(networks_supported.items[i])) { 31148c2ecf20Sopenharmony_ci case NDIS_80211_TYPE_FREQ_HOP: 31158c2ecf20Sopenharmony_ci case NDIS_80211_TYPE_DIRECT_SEQ: 31168c2ecf20Sopenharmony_ci priv->caps |= CAP_MODE_80211B; 31178c2ecf20Sopenharmony_ci break; 31188c2ecf20Sopenharmony_ci case NDIS_80211_TYPE_OFDM_A: 31198c2ecf20Sopenharmony_ci priv->caps |= CAP_MODE_80211A; 31208c2ecf20Sopenharmony_ci break; 31218c2ecf20Sopenharmony_ci case NDIS_80211_TYPE_OFDM_G: 31228c2ecf20Sopenharmony_ci priv->caps |= CAP_MODE_80211G; 31238c2ecf20Sopenharmony_ci break; 31248c2ecf20Sopenharmony_ci } 31258c2ecf20Sopenharmony_ci } 31268c2ecf20Sopenharmony_ci } 31278c2ecf20Sopenharmony_ci 31288c2ecf20Sopenharmony_ci /* get device 802.11 capabilities, number of PMKIDs */ 31298c2ecf20Sopenharmony_ci len = sizeof(caps); 31308c2ecf20Sopenharmony_ci retval = rndis_query_oid(usbdev, 31318c2ecf20Sopenharmony_ci RNDIS_OID_802_11_CAPABILITY, 31328c2ecf20Sopenharmony_ci &caps, &len); 31338c2ecf20Sopenharmony_ci if (!retval) { 31348c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "RNDIS_OID_802_11_CAPABILITY -> len %d, " 31358c2ecf20Sopenharmony_ci "ver %d, pmkids %d, auth-encr-pairs %d\n", 31368c2ecf20Sopenharmony_ci le32_to_cpu(caps.length), 31378c2ecf20Sopenharmony_ci le32_to_cpu(caps.version), 31388c2ecf20Sopenharmony_ci le32_to_cpu(caps.num_pmkids), 31398c2ecf20Sopenharmony_ci le32_to_cpu(caps.num_auth_encr_pair)); 31408c2ecf20Sopenharmony_ci wiphy->max_num_pmkids = le32_to_cpu(caps.num_pmkids); 31418c2ecf20Sopenharmony_ci } else 31428c2ecf20Sopenharmony_ci wiphy->max_num_pmkids = 0; 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci return retval; 31458c2ecf20Sopenharmony_ci} 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_cistatic void rndis_do_cqm(struct usbnet *usbdev, s32 rssi) 31488c2ecf20Sopenharmony_ci{ 31498c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 31508c2ecf20Sopenharmony_ci enum nl80211_cqm_rssi_threshold_event event; 31518c2ecf20Sopenharmony_ci int thold, hyst, last_event; 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_ci if (priv->cqm_rssi_thold >= 0 || rssi >= 0) 31548c2ecf20Sopenharmony_ci return; 31558c2ecf20Sopenharmony_ci if (priv->infra_mode != NDIS_80211_INFRA_INFRA) 31568c2ecf20Sopenharmony_ci return; 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci last_event = priv->last_cqm_event_rssi; 31598c2ecf20Sopenharmony_ci thold = priv->cqm_rssi_thold; 31608c2ecf20Sopenharmony_ci hyst = priv->cqm_rssi_hyst; 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci if (rssi < thold && (last_event == 0 || rssi < last_event - hyst)) 31638c2ecf20Sopenharmony_ci event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; 31648c2ecf20Sopenharmony_ci else if (rssi > thold && (last_event == 0 || rssi > last_event + hyst)) 31658c2ecf20Sopenharmony_ci event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; 31668c2ecf20Sopenharmony_ci else 31678c2ecf20Sopenharmony_ci return; 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci priv->last_cqm_event_rssi = rssi; 31708c2ecf20Sopenharmony_ci cfg80211_cqm_rssi_notify(usbdev->net, event, rssi, GFP_KERNEL); 31718c2ecf20Sopenharmony_ci} 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci#define DEVICE_POLLER_JIFFIES (HZ) 31748c2ecf20Sopenharmony_cistatic void rndis_device_poller(struct work_struct *work) 31758c2ecf20Sopenharmony_ci{ 31768c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = 31778c2ecf20Sopenharmony_ci container_of(work, struct rndis_wlan_private, 31788c2ecf20Sopenharmony_ci dev_poller_work.work); 31798c2ecf20Sopenharmony_ci struct usbnet *usbdev = priv->usbdev; 31808c2ecf20Sopenharmony_ci __le32 rssi, tmp; 31818c2ecf20Sopenharmony_ci int len, ret, j; 31828c2ecf20Sopenharmony_ci int update_jiffies = DEVICE_POLLER_JIFFIES; 31838c2ecf20Sopenharmony_ci void *buf; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci /* Only check/do workaround when connected. Calling is_associated() 31868c2ecf20Sopenharmony_ci * also polls device with rndis_command() and catches for media link 31878c2ecf20Sopenharmony_ci * indications. 31888c2ecf20Sopenharmony_ci */ 31898c2ecf20Sopenharmony_ci if (!is_associated(usbdev)) { 31908c2ecf20Sopenharmony_ci /* Workaround bad scanning in BCM4320a devices with active 31918c2ecf20Sopenharmony_ci * background scanning when not associated. 31928c2ecf20Sopenharmony_ci */ 31938c2ecf20Sopenharmony_ci if (priv->device_type == RNDIS_BCM4320A && priv->radio_on && 31948c2ecf20Sopenharmony_ci !priv->scan_request) { 31958c2ecf20Sopenharmony_ci /* Get previous scan results */ 31968c2ecf20Sopenharmony_ci rndis_check_bssid_list(usbdev, NULL, NULL); 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci /* Initiate new scan */ 31998c2ecf20Sopenharmony_ci rndis_start_bssid_list_scan(usbdev); 32008c2ecf20Sopenharmony_ci } 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci goto end; 32038c2ecf20Sopenharmony_ci } 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci len = sizeof(rssi); 32068c2ecf20Sopenharmony_ci ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, 32078c2ecf20Sopenharmony_ci &rssi, &len); 32088c2ecf20Sopenharmony_ci if (ret == 0) { 32098c2ecf20Sopenharmony_ci priv->last_qual = level_to_qual(le32_to_cpu(rssi)); 32108c2ecf20Sopenharmony_ci rndis_do_cqm(usbdev, le32_to_cpu(rssi)); 32118c2ecf20Sopenharmony_ci } 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "dev-poller: RNDIS_OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", 32148c2ecf20Sopenharmony_ci ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci /* Workaround transfer stalls on poor quality links. 32178c2ecf20Sopenharmony_ci * TODO: find right way to fix these stalls (as stalls do not happen 32188c2ecf20Sopenharmony_ci * with ndiswrapper/windows driver). */ 32198c2ecf20Sopenharmony_ci if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { 32208c2ecf20Sopenharmony_ci /* Decrease stats worker interval to catch stalls. 32218c2ecf20Sopenharmony_ci * faster. Faster than 400-500ms causes packet loss, 32228c2ecf20Sopenharmony_ci * Slower doesn't catch stalls fast enough. 32238c2ecf20Sopenharmony_ci */ 32248c2ecf20Sopenharmony_ci j = msecs_to_jiffies(priv->param_workaround_interval); 32258c2ecf20Sopenharmony_ci if (j > DEVICE_POLLER_JIFFIES) 32268c2ecf20Sopenharmony_ci j = DEVICE_POLLER_JIFFIES; 32278c2ecf20Sopenharmony_ci else if (j <= 0) 32288c2ecf20Sopenharmony_ci j = 1; 32298c2ecf20Sopenharmony_ci update_jiffies = j; 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_ci /* Send scan OID. Use of both OIDs is required to get device 32328c2ecf20Sopenharmony_ci * working. 32338c2ecf20Sopenharmony_ci */ 32348c2ecf20Sopenharmony_ci tmp = cpu_to_le32(1); 32358c2ecf20Sopenharmony_ci rndis_set_oid(usbdev, 32368c2ecf20Sopenharmony_ci RNDIS_OID_802_11_BSSID_LIST_SCAN, 32378c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ci len = CONTROL_BUFFER_SIZE; 32408c2ecf20Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 32418c2ecf20Sopenharmony_ci if (!buf) 32428c2ecf20Sopenharmony_ci goto end; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci rndis_query_oid(usbdev, 32458c2ecf20Sopenharmony_ci RNDIS_OID_802_11_BSSID_LIST, 32468c2ecf20Sopenharmony_ci buf, &len); 32478c2ecf20Sopenharmony_ci kfree(buf); 32488c2ecf20Sopenharmony_ci } 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ciend: 32518c2ecf20Sopenharmony_ci if (update_jiffies >= HZ) 32528c2ecf20Sopenharmony_ci update_jiffies = round_jiffies_relative(update_jiffies); 32538c2ecf20Sopenharmony_ci else { 32548c2ecf20Sopenharmony_ci j = round_jiffies_relative(update_jiffies); 32558c2ecf20Sopenharmony_ci if (abs(j - update_jiffies) <= 10) 32568c2ecf20Sopenharmony_ci update_jiffies = j; 32578c2ecf20Sopenharmony_ci } 32588c2ecf20Sopenharmony_ci 32598c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->dev_poller_work, 32608c2ecf20Sopenharmony_ci update_jiffies); 32618c2ecf20Sopenharmony_ci} 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci/* 32648c2ecf20Sopenharmony_ci * driver/device initialization 32658c2ecf20Sopenharmony_ci */ 32668c2ecf20Sopenharmony_cistatic void rndis_copy_module_params(struct usbnet *usbdev, int device_type) 32678c2ecf20Sopenharmony_ci{ 32688c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci priv->device_type = device_type; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci priv->param_country[0] = modparam_country[0]; 32738c2ecf20Sopenharmony_ci priv->param_country[1] = modparam_country[1]; 32748c2ecf20Sopenharmony_ci priv->param_country[2] = 0; 32758c2ecf20Sopenharmony_ci priv->param_frameburst = modparam_frameburst; 32768c2ecf20Sopenharmony_ci priv->param_afterburner = modparam_afterburner; 32778c2ecf20Sopenharmony_ci priv->param_power_save = modparam_power_save; 32788c2ecf20Sopenharmony_ci priv->param_power_output = modparam_power_output; 32798c2ecf20Sopenharmony_ci priv->param_roamtrigger = modparam_roamtrigger; 32808c2ecf20Sopenharmony_ci priv->param_roamdelta = modparam_roamdelta; 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci priv->param_country[0] = toupper(priv->param_country[0]); 32838c2ecf20Sopenharmony_ci priv->param_country[1] = toupper(priv->param_country[1]); 32848c2ecf20Sopenharmony_ci /* doesn't support EU as country code, use FI instead */ 32858c2ecf20Sopenharmony_ci if (!strcmp(priv->param_country, "EU")) 32868c2ecf20Sopenharmony_ci strcpy(priv->param_country, "FI"); 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci if (priv->param_power_save < 0) 32898c2ecf20Sopenharmony_ci priv->param_power_save = 0; 32908c2ecf20Sopenharmony_ci else if (priv->param_power_save > 2) 32918c2ecf20Sopenharmony_ci priv->param_power_save = 2; 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci if (priv->param_power_output < 0) 32948c2ecf20Sopenharmony_ci priv->param_power_output = 0; 32958c2ecf20Sopenharmony_ci else if (priv->param_power_output > 3) 32968c2ecf20Sopenharmony_ci priv->param_power_output = 3; 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci if (priv->param_roamtrigger < -80) 32998c2ecf20Sopenharmony_ci priv->param_roamtrigger = -80; 33008c2ecf20Sopenharmony_ci else if (priv->param_roamtrigger > -60) 33018c2ecf20Sopenharmony_ci priv->param_roamtrigger = -60; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci if (priv->param_roamdelta < 0) 33048c2ecf20Sopenharmony_ci priv->param_roamdelta = 0; 33058c2ecf20Sopenharmony_ci else if (priv->param_roamdelta > 2) 33068c2ecf20Sopenharmony_ci priv->param_roamdelta = 2; 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_ci if (modparam_workaround_interval < 0) 33098c2ecf20Sopenharmony_ci priv->param_workaround_interval = 500; 33108c2ecf20Sopenharmony_ci else 33118c2ecf20Sopenharmony_ci priv->param_workaround_interval = modparam_workaround_interval; 33128c2ecf20Sopenharmony_ci} 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_cistatic int unknown_early_init(struct usbnet *usbdev) 33158c2ecf20Sopenharmony_ci{ 33168c2ecf20Sopenharmony_ci /* copy module parameters for unknown so that iwconfig reports txpower 33178c2ecf20Sopenharmony_ci * and workaround parameter is copied to private structure correctly. 33188c2ecf20Sopenharmony_ci */ 33198c2ecf20Sopenharmony_ci rndis_copy_module_params(usbdev, RNDIS_UNKNOWN); 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci /* This is unknown device, so do not try set configuration parameters. 33228c2ecf20Sopenharmony_ci */ 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci return 0; 33258c2ecf20Sopenharmony_ci} 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_cistatic int bcm4320a_early_init(struct usbnet *usbdev) 33288c2ecf20Sopenharmony_ci{ 33298c2ecf20Sopenharmony_ci /* copy module parameters for bcm4320a so that iwconfig reports txpower 33308c2ecf20Sopenharmony_ci * and workaround parameter is copied to private structure correctly. 33318c2ecf20Sopenharmony_ci */ 33328c2ecf20Sopenharmony_ci rndis_copy_module_params(usbdev, RNDIS_BCM4320A); 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci /* bcm4320a doesn't handle configuration parameters well. Try 33358c2ecf20Sopenharmony_ci * set any and you get partially zeroed mac and broken device. 33368c2ecf20Sopenharmony_ci */ 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci return 0; 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_cistatic int bcm4320b_early_init(struct usbnet *usbdev) 33428c2ecf20Sopenharmony_ci{ 33438c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 33448c2ecf20Sopenharmony_ci char buf[8]; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci rndis_copy_module_params(usbdev, RNDIS_BCM4320B); 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci /* Early initialization settings, setting these won't have effect 33498c2ecf20Sopenharmony_ci * if called after generic_rndis_bind(). 33508c2ecf20Sopenharmony_ci */ 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "Country", priv->param_country); 33538c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "FrameBursting", 33548c2ecf20Sopenharmony_ci priv->param_frameburst ? "1" : "0"); 33558c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "Afterburner", 33568c2ecf20Sopenharmony_ci priv->param_afterburner ? "1" : "0"); 33578c2ecf20Sopenharmony_ci sprintf(buf, "%d", priv->param_power_save); 33588c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf); 33598c2ecf20Sopenharmony_ci sprintf(buf, "%d", priv->param_power_output); 33608c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "PwrOut", buf); 33618c2ecf20Sopenharmony_ci sprintf(buf, "%d", priv->param_roamtrigger); 33628c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf); 33638c2ecf20Sopenharmony_ci sprintf(buf, "%d", priv->param_roamdelta); 33648c2ecf20Sopenharmony_ci rndis_set_config_parameter_str(usbdev, "RoamDelta", buf); 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci return 0; 33678c2ecf20Sopenharmony_ci} 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci/* same as rndis_netdev_ops but with local multicast handler */ 33708c2ecf20Sopenharmony_cistatic const struct net_device_ops rndis_wlan_netdev_ops = { 33718c2ecf20Sopenharmony_ci .ndo_open = usbnet_open, 33728c2ecf20Sopenharmony_ci .ndo_stop = usbnet_stop, 33738c2ecf20Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 33748c2ecf20Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 33758c2ecf20Sopenharmony_ci .ndo_get_stats64 = usbnet_get_stats64, 33768c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 33778c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 33788c2ecf20Sopenharmony_ci .ndo_set_rx_mode = rndis_wlan_set_multicast_list, 33798c2ecf20Sopenharmony_ci}; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_cistatic int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) 33828c2ecf20Sopenharmony_ci{ 33838c2ecf20Sopenharmony_ci struct wiphy *wiphy; 33848c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv; 33858c2ecf20Sopenharmony_ci int retval, len; 33868c2ecf20Sopenharmony_ci __le32 tmp; 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci /* allocate wiphy and rndis private data 33898c2ecf20Sopenharmony_ci * NOTE: We only support a single virtual interface, so wiphy 33908c2ecf20Sopenharmony_ci * and wireless_dev are somewhat synonymous for this device. 33918c2ecf20Sopenharmony_ci */ 33928c2ecf20Sopenharmony_ci wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private)); 33938c2ecf20Sopenharmony_ci if (!wiphy) 33948c2ecf20Sopenharmony_ci return -ENOMEM; 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci priv = wiphy_priv(wiphy); 33978c2ecf20Sopenharmony_ci usbdev->net->ieee80211_ptr = &priv->wdev; 33988c2ecf20Sopenharmony_ci priv->wdev.wiphy = wiphy; 33998c2ecf20Sopenharmony_ci priv->wdev.iftype = NL80211_IFTYPE_STATION; 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci /* These have to be initialized before calling generic_rndis_bind(). 34028c2ecf20Sopenharmony_ci * Otherwise we'll be in big trouble in rndis_wlan_early_init(). 34038c2ecf20Sopenharmony_ci */ 34048c2ecf20Sopenharmony_ci usbdev->driver_priv = priv; 34058c2ecf20Sopenharmony_ci priv->usbdev = usbdev; 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci mutex_init(&priv->command_lock); 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci /* because rndis_command() sleeps we need to use workqueue */ 34108c2ecf20Sopenharmony_ci priv->workqueue = create_singlethread_workqueue("rndis_wlan"); 34118c2ecf20Sopenharmony_ci if (!priv->workqueue) { 34128c2ecf20Sopenharmony_ci wiphy_free(wiphy); 34138c2ecf20Sopenharmony_ci return -ENOMEM; 34148c2ecf20Sopenharmony_ci } 34158c2ecf20Sopenharmony_ci INIT_WORK(&priv->work, rndis_wlan_worker); 34168c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); 34178c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci /* try bind rndis_host */ 34208c2ecf20Sopenharmony_ci retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS); 34218c2ecf20Sopenharmony_ci if (retval < 0) 34228c2ecf20Sopenharmony_ci goto fail; 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_ci /* generic_rndis_bind set packet filter to multicast_all+ 34258c2ecf20Sopenharmony_ci * promisc mode which doesn't work well for our devices (device 34268c2ecf20Sopenharmony_ci * picks up rssi to closest station instead of to access point). 34278c2ecf20Sopenharmony_ci * 34288c2ecf20Sopenharmony_ci * rndis_host wants to avoid all OID as much as possible 34298c2ecf20Sopenharmony_ci * so do promisc/multicast handling in rndis_wlan. 34308c2ecf20Sopenharmony_ci */ 34318c2ecf20Sopenharmony_ci usbdev->net->netdev_ops = &rndis_wlan_netdev_ops; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci tmp = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); 34348c2ecf20Sopenharmony_ci retval = rndis_set_oid(usbdev, 34358c2ecf20Sopenharmony_ci RNDIS_OID_GEN_CURRENT_PACKET_FILTER, 34368c2ecf20Sopenharmony_ci &tmp, sizeof(tmp)); 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci len = sizeof(tmp); 34398c2ecf20Sopenharmony_ci retval = rndis_query_oid(usbdev, 34408c2ecf20Sopenharmony_ci RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, 34418c2ecf20Sopenharmony_ci &tmp, &len); 34428c2ecf20Sopenharmony_ci priv->multicast_size = le32_to_cpu(tmp); 34438c2ecf20Sopenharmony_ci if (retval < 0 || priv->multicast_size < 0) 34448c2ecf20Sopenharmony_ci priv->multicast_size = 0; 34458c2ecf20Sopenharmony_ci if (priv->multicast_size > 0) 34468c2ecf20Sopenharmony_ci usbdev->net->flags |= IFF_MULTICAST; 34478c2ecf20Sopenharmony_ci else 34488c2ecf20Sopenharmony_ci usbdev->net->flags &= ~IFF_MULTICAST; 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci /* fill-out wiphy structure and register w/ cfg80211 */ 34518c2ecf20Sopenharmony_ci memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); 34528c2ecf20Sopenharmony_ci wiphy->privid = rndis_wiphy_privid; 34538c2ecf20Sopenharmony_ci wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) 34548c2ecf20Sopenharmony_ci | BIT(NL80211_IFTYPE_ADHOC); 34558c2ecf20Sopenharmony_ci wiphy->max_scan_ssids = 1; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci /* TODO: fill-out band/encr information based on priv->caps */ 34588c2ecf20Sopenharmony_ci rndis_wlan_get_caps(usbdev, wiphy); 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); 34618c2ecf20Sopenharmony_ci memcpy(priv->rates, rndis_rates, sizeof(rndis_rates)); 34628c2ecf20Sopenharmony_ci priv->band.channels = priv->channels; 34638c2ecf20Sopenharmony_ci priv->band.n_channels = ARRAY_SIZE(rndis_channels); 34648c2ecf20Sopenharmony_ci priv->band.bitrates = priv->rates; 34658c2ecf20Sopenharmony_ci priv->band.n_bitrates = ARRAY_SIZE(rndis_rates); 34668c2ecf20Sopenharmony_ci wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 34678c2ecf20Sopenharmony_ci wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci memcpy(priv->cipher_suites, rndis_cipher_suites, 34708c2ecf20Sopenharmony_ci sizeof(rndis_cipher_suites)); 34718c2ecf20Sopenharmony_ci wiphy->cipher_suites = priv->cipher_suites; 34728c2ecf20Sopenharmony_ci wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites); 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci set_wiphy_dev(wiphy, &usbdev->udev->dev); 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci if (wiphy_register(wiphy)) { 34778c2ecf20Sopenharmony_ci retval = -ENODEV; 34788c2ecf20Sopenharmony_ci goto fail; 34798c2ecf20Sopenharmony_ci } 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci set_default_iw_params(usbdev); 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_ci priv->power_mode = -1; 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_ci /* set default rts/frag */ 34868c2ecf20Sopenharmony_ci rndis_set_wiphy_params(wiphy, 34878c2ecf20Sopenharmony_ci WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci /* turn radio off on init */ 34908c2ecf20Sopenharmony_ci priv->radio_on = false; 34918c2ecf20Sopenharmony_ci disassociate(usbdev, false); 34928c2ecf20Sopenharmony_ci netif_carrier_off(usbdev->net); 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci return 0; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_cifail: 34978c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->dev_poller_work); 34988c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 34998c2ecf20Sopenharmony_ci cancel_work_sync(&priv->work); 35008c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 35018c2ecf20Sopenharmony_ci destroy_workqueue(priv->workqueue); 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci wiphy_free(wiphy); 35048c2ecf20Sopenharmony_ci return retval; 35058c2ecf20Sopenharmony_ci} 35068c2ecf20Sopenharmony_ci 35078c2ecf20Sopenharmony_cistatic void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) 35088c2ecf20Sopenharmony_ci{ 35098c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci /* turn radio off */ 35128c2ecf20Sopenharmony_ci disassociate(usbdev, false); 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->dev_poller_work); 35158c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 35168c2ecf20Sopenharmony_ci cancel_work_sync(&priv->work); 35178c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 35188c2ecf20Sopenharmony_ci destroy_workqueue(priv->workqueue); 35198c2ecf20Sopenharmony_ci 35208c2ecf20Sopenharmony_ci rndis_unbind(usbdev, intf); 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci wiphy_unregister(priv->wdev.wiphy); 35238c2ecf20Sopenharmony_ci wiphy_free(priv->wdev.wiphy); 35248c2ecf20Sopenharmony_ci} 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_cistatic int rndis_wlan_reset(struct usbnet *usbdev) 35278c2ecf20Sopenharmony_ci{ 35288c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 35298c2ecf20Sopenharmony_ci int retval; 35308c2ecf20Sopenharmony_ci 35318c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s()\n", __func__); 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci retval = rndis_reset(usbdev); 35348c2ecf20Sopenharmony_ci if (retval) 35358c2ecf20Sopenharmony_ci netdev_warn(usbdev->net, "rndis_reset failed: %d\n", retval); 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci /* rndis_reset cleared multicast list, so restore here. 35388c2ecf20Sopenharmony_ci (set_multicast_list() also turns on current packet filter) */ 35398c2ecf20Sopenharmony_ci set_multicast_list(usbdev); 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_ci queue_delayed_work(priv->workqueue, &priv->dev_poller_work, 35428c2ecf20Sopenharmony_ci round_jiffies_relative(DEVICE_POLLER_JIFFIES)); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci return deauthenticate(usbdev); 35458c2ecf20Sopenharmony_ci} 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_cistatic int rndis_wlan_stop(struct usbnet *usbdev) 35488c2ecf20Sopenharmony_ci{ 35498c2ecf20Sopenharmony_ci struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 35508c2ecf20Sopenharmony_ci int retval; 35518c2ecf20Sopenharmony_ci __le32 filter; 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci netdev_dbg(usbdev->net, "%s()\n", __func__); 35548c2ecf20Sopenharmony_ci 35558c2ecf20Sopenharmony_ci retval = disassociate(usbdev, false); 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci priv->work_pending = 0; 35588c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->dev_poller_work); 35598c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 35608c2ecf20Sopenharmony_ci cancel_work_sync(&priv->work); 35618c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci if (priv->scan_request) { 35648c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = { 35658c2ecf20Sopenharmony_ci .aborted = true, 35668c2ecf20Sopenharmony_ci }; 35678c2ecf20Sopenharmony_ci 35688c2ecf20Sopenharmony_ci cfg80211_scan_done(priv->scan_request, &info); 35698c2ecf20Sopenharmony_ci priv->scan_request = NULL; 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_ci /* Set current packet filter zero to block receiving data packets from 35738c2ecf20Sopenharmony_ci device. */ 35748c2ecf20Sopenharmony_ci filter = 0; 35758c2ecf20Sopenharmony_ci rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, 35768c2ecf20Sopenharmony_ci sizeof(filter)); 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci return retval; 35798c2ecf20Sopenharmony_ci} 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_cistatic const struct driver_info bcm4320b_info = { 35828c2ecf20Sopenharmony_ci .description = "Wireless RNDIS device, BCM4320b based", 35838c2ecf20Sopenharmony_ci .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | 35848c2ecf20Sopenharmony_ci FLAG_AVOID_UNLINK_URBS, 35858c2ecf20Sopenharmony_ci .bind = rndis_wlan_bind, 35868c2ecf20Sopenharmony_ci .unbind = rndis_wlan_unbind, 35878c2ecf20Sopenharmony_ci .status = rndis_status, 35888c2ecf20Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 35898c2ecf20Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 35908c2ecf20Sopenharmony_ci .reset = rndis_wlan_reset, 35918c2ecf20Sopenharmony_ci .stop = rndis_wlan_stop, 35928c2ecf20Sopenharmony_ci .early_init = bcm4320b_early_init, 35938c2ecf20Sopenharmony_ci .indication = rndis_wlan_indication, 35948c2ecf20Sopenharmony_ci}; 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_cistatic const struct driver_info bcm4320a_info = { 35978c2ecf20Sopenharmony_ci .description = "Wireless RNDIS device, BCM4320a based", 35988c2ecf20Sopenharmony_ci .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | 35998c2ecf20Sopenharmony_ci FLAG_AVOID_UNLINK_URBS, 36008c2ecf20Sopenharmony_ci .bind = rndis_wlan_bind, 36018c2ecf20Sopenharmony_ci .unbind = rndis_wlan_unbind, 36028c2ecf20Sopenharmony_ci .status = rndis_status, 36038c2ecf20Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 36048c2ecf20Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 36058c2ecf20Sopenharmony_ci .reset = rndis_wlan_reset, 36068c2ecf20Sopenharmony_ci .stop = rndis_wlan_stop, 36078c2ecf20Sopenharmony_ci .early_init = bcm4320a_early_init, 36088c2ecf20Sopenharmony_ci .indication = rndis_wlan_indication, 36098c2ecf20Sopenharmony_ci}; 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_cistatic const struct driver_info rndis_wlan_info = { 36128c2ecf20Sopenharmony_ci .description = "Wireless RNDIS device", 36138c2ecf20Sopenharmony_ci .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | 36148c2ecf20Sopenharmony_ci FLAG_AVOID_UNLINK_URBS, 36158c2ecf20Sopenharmony_ci .bind = rndis_wlan_bind, 36168c2ecf20Sopenharmony_ci .unbind = rndis_wlan_unbind, 36178c2ecf20Sopenharmony_ci .status = rndis_status, 36188c2ecf20Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 36198c2ecf20Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 36208c2ecf20Sopenharmony_ci .reset = rndis_wlan_reset, 36218c2ecf20Sopenharmony_ci .stop = rndis_wlan_stop, 36228c2ecf20Sopenharmony_ci .early_init = unknown_early_init, 36238c2ecf20Sopenharmony_ci .indication = rndis_wlan_indication, 36248c2ecf20Sopenharmony_ci}; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_cistatic const struct usb_device_id products [] = { 36298c2ecf20Sopenharmony_ci#define RNDIS_MASTER_INTERFACE \ 36308c2ecf20Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, \ 36318c2ecf20Sopenharmony_ci .bInterfaceSubClass = 2 /* ACM */, \ 36328c2ecf20Sopenharmony_ci .bInterfaceProtocol = 0x0ff 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom 36358c2ecf20Sopenharmony_ci * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki. 36368c2ecf20Sopenharmony_ci */ 36378c2ecf20Sopenharmony_ci{ 36388c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36398c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36408c2ecf20Sopenharmony_ci .idVendor = 0x0411, 36418c2ecf20Sopenharmony_ci .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */ 36428c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36438c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36448c2ecf20Sopenharmony_ci}, { 36458c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36468c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36478c2ecf20Sopenharmony_ci .idVendor = 0x0baf, 36488c2ecf20Sopenharmony_ci .idProduct = 0x011b, /* U.S. Robotics USR5421 */ 36498c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36508c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36518c2ecf20Sopenharmony_ci}, { 36528c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36538c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36548c2ecf20Sopenharmony_ci .idVendor = 0x050d, 36558c2ecf20Sopenharmony_ci .idProduct = 0x011b, /* Belkin F5D7051 */ 36568c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36578c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36588c2ecf20Sopenharmony_ci}, { 36598c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36608c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36618c2ecf20Sopenharmony_ci .idVendor = 0x1799, /* Belkin has two vendor ids */ 36628c2ecf20Sopenharmony_ci .idProduct = 0x011b, /* Belkin F5D7051 */ 36638c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36648c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36658c2ecf20Sopenharmony_ci}, { 36668c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36678c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36688c2ecf20Sopenharmony_ci .idVendor = 0x13b1, 36698c2ecf20Sopenharmony_ci .idProduct = 0x0014, /* Linksys WUSB54GSv2 */ 36708c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36718c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36728c2ecf20Sopenharmony_ci}, { 36738c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36748c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36758c2ecf20Sopenharmony_ci .idVendor = 0x13b1, 36768c2ecf20Sopenharmony_ci .idProduct = 0x0026, /* Linksys WUSB54GSC */ 36778c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36788c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36798c2ecf20Sopenharmony_ci}, { 36808c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36818c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36828c2ecf20Sopenharmony_ci .idVendor = 0x0b05, 36838c2ecf20Sopenharmony_ci .idProduct = 0x1717, /* Asus WL169gE */ 36848c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36858c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36868c2ecf20Sopenharmony_ci}, { 36878c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36888c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36898c2ecf20Sopenharmony_ci .idVendor = 0x0a5c, 36908c2ecf20Sopenharmony_ci .idProduct = 0xd11b, /* Eminent EM4045 */ 36918c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36928c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 36938c2ecf20Sopenharmony_ci}, { 36948c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 36958c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 36968c2ecf20Sopenharmony_ci .idVendor = 0x1690, 36978c2ecf20Sopenharmony_ci .idProduct = 0x0715, /* BT Voyager 1055 */ 36988c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 36998c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320b_info, 37008c2ecf20Sopenharmony_ci}, 37018c2ecf20Sopenharmony_ci/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom 37028c2ecf20Sopenharmony_ci * parameters available, hardware probably contain older firmware version with 37038c2ecf20Sopenharmony_ci * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki. 37048c2ecf20Sopenharmony_ci */ 37058c2ecf20Sopenharmony_ci{ 37068c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 37078c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 37088c2ecf20Sopenharmony_ci .idVendor = 0x13b1, 37098c2ecf20Sopenharmony_ci .idProduct = 0x000e, /* Linksys WUSB54GSv1 */ 37108c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 37118c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320a_info, 37128c2ecf20Sopenharmony_ci}, { 37138c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 37148c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 37158c2ecf20Sopenharmony_ci .idVendor = 0x0baf, 37168c2ecf20Sopenharmony_ci .idProduct = 0x0111, /* U.S. Robotics USR5420 */ 37178c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 37188c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320a_info, 37198c2ecf20Sopenharmony_ci}, { 37208c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 37218c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 37228c2ecf20Sopenharmony_ci .idVendor = 0x0411, 37238c2ecf20Sopenharmony_ci .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */ 37248c2ecf20Sopenharmony_ci RNDIS_MASTER_INTERFACE, 37258c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &bcm4320a_info, 37268c2ecf20Sopenharmony_ci}, 37278c2ecf20Sopenharmony_ci/* Generic Wireless RNDIS devices that we don't have exact 37288c2ecf20Sopenharmony_ci * idVendor/idProduct/chip yet. 37298c2ecf20Sopenharmony_ci */ 37308c2ecf20Sopenharmony_ci{ 37318c2ecf20Sopenharmony_ci /* RNDIS is MSFT's un-official variant of CDC ACM */ 37328c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 37338c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_wlan_info, 37348c2ecf20Sopenharmony_ci}, { 37358c2ecf20Sopenharmony_ci /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ 37368c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), 37378c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_wlan_info, 37388c2ecf20Sopenharmony_ci}, 37398c2ecf20Sopenharmony_ci { }, // END 37408c2ecf20Sopenharmony_ci}; 37418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_cistatic struct usb_driver rndis_wlan_driver = { 37448c2ecf20Sopenharmony_ci .name = "rndis_wlan", 37458c2ecf20Sopenharmony_ci .id_table = products, 37468c2ecf20Sopenharmony_ci .probe = usbnet_probe, 37478c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 37488c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 37498c2ecf20Sopenharmony_ci .resume = usbnet_resume, 37508c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 37518c2ecf20Sopenharmony_ci}; 37528c2ecf20Sopenharmony_ci 37538c2ecf20Sopenharmony_cimodule_usb_driver(rndis_wlan_driver); 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bjorge Dijkstra"); 37568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jussi Kivilinna"); 37578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters"); 37588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 37598c2ecf20Sopenharmony_ci 3760