162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. 462306a36Sopenharmony_ci * All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "netdev.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define WILC_HIF_SCAN_TIMEOUT_MS 5000 1062306a36Sopenharmony_ci#define WILC_HIF_CONNECT_TIMEOUT_MS 9500 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define WILC_FALSE_FRMWR_CHANNEL 100 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define WILC_SCAN_WID_LIST_SIZE 6 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct wilc_rcvd_mac_info { 1762306a36Sopenharmony_ci u8 status; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct wilc_set_multicast { 2162306a36Sopenharmony_ci u32 enabled; 2262306a36Sopenharmony_ci u32 cnt; 2362306a36Sopenharmony_ci u8 *mc_list; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct host_if_wowlan_trigger { 2762306a36Sopenharmony_ci u8 wowlan_trigger; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct wilc_del_all_sta { 3162306a36Sopenharmony_ci u8 assoc_sta; 3262306a36Sopenharmony_ci u8 mac[WILC_MAX_NUM_STA][ETH_ALEN]; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciunion wilc_message_body { 3662306a36Sopenharmony_ci struct wilc_rcvd_net_info net_info; 3762306a36Sopenharmony_ci struct wilc_rcvd_mac_info mac_info; 3862306a36Sopenharmony_ci struct wilc_set_multicast mc_info; 3962306a36Sopenharmony_ci struct wilc_remain_ch remain_on_ch; 4062306a36Sopenharmony_ci char *data; 4162306a36Sopenharmony_ci struct host_if_wowlan_trigger wow_trigger; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct host_if_msg { 4562306a36Sopenharmony_ci union wilc_message_body body; 4662306a36Sopenharmony_ci struct wilc_vif *vif; 4762306a36Sopenharmony_ci struct work_struct work; 4862306a36Sopenharmony_ci void (*fn)(struct work_struct *ws); 4962306a36Sopenharmony_ci struct completion work_comp; 5062306a36Sopenharmony_ci bool is_sync; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 'msg' should be free by the caller for syc */ 5462306a36Sopenharmony_cistatic struct host_if_msg* 5562306a36Sopenharmony_ciwilc_alloc_work(struct wilc_vif *vif, void (*work_fun)(struct work_struct *), 5662306a36Sopenharmony_ci bool is_sync) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct host_if_msg *msg; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!work_fun) 6162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci msg = kzalloc(sizeof(*msg), GFP_ATOMIC); 6462306a36Sopenharmony_ci if (!msg) 6562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6662306a36Sopenharmony_ci msg->fn = work_fun; 6762306a36Sopenharmony_ci msg->vif = vif; 6862306a36Sopenharmony_ci msg->is_sync = is_sync; 6962306a36Sopenharmony_ci if (is_sync) 7062306a36Sopenharmony_ci init_completion(&msg->work_comp); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return msg; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int wilc_enqueue_work(struct host_if_msg *msg) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci INIT_WORK(&msg->work, msg->fn); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!msg->vif || !msg->vif->wilc || !msg->vif->wilc->hif_workqueue) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!queue_work(msg->vif->wilc->hif_workqueue, &msg->work)) 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* The idx starts from 0 to (NUM_CONCURRENT_IFC - 1), but 0 index used as 8962306a36Sopenharmony_ci * special purpose in wilc device, so we add 1 to the index to starts from 1. 9062306a36Sopenharmony_ci * As a result, the returned index will be 1 to NUM_CONCURRENT_IFC. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ciint wilc_get_vif_idx(struct wilc_vif *vif) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci return vif->idx + 1; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* We need to minus 1 from idx which is from wilc device to get real index 9862306a36Sopenharmony_ci * of wilc->vif[], because we add 1 when pass to wilc device in the function 9962306a36Sopenharmony_ci * wilc_get_vif_idx. 10062306a36Sopenharmony_ci * As a result, the index should be between 0 and (NUM_CONCURRENT_IFC - 1). 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int index = idx - 1; 10562306a36Sopenharmony_ci struct wilc_vif *vif; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC) 10862306a36Sopenharmony_ci return NULL; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci list_for_each_entry_rcu(vif, &wilc->vif_list, list) { 11162306a36Sopenharmony_ci if (vif->idx == index) 11262306a36Sopenharmony_ci return vif; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return NULL; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int handle_scan_done(struct wilc_vif *vif, enum scan_event evt) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci int result = 0; 12162306a36Sopenharmony_ci u8 abort_running_scan; 12262306a36Sopenharmony_ci struct wid wid; 12362306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 12462306a36Sopenharmony_ci struct wilc_user_scan_req *scan_req; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (evt == SCAN_EVENT_ABORTED) { 12762306a36Sopenharmony_ci abort_running_scan = 1; 12862306a36Sopenharmony_ci wid.id = WID_ABORT_RUNNING_SCAN; 12962306a36Sopenharmony_ci wid.type = WID_CHAR; 13062306a36Sopenharmony_ci wid.val = (s8 *)&abort_running_scan; 13162306a36Sopenharmony_ci wid.size = sizeof(char); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 13462306a36Sopenharmony_ci if (result) { 13562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set abort running\n"); 13662306a36Sopenharmony_ci result = -EFAULT; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!hif_drv) { 14162306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__); 14262306a36Sopenharmony_ci return result; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci scan_req = &hif_drv->usr_scan_req; 14662306a36Sopenharmony_ci if (scan_req->scan_result) { 14762306a36Sopenharmony_ci scan_req->scan_result(evt, NULL, scan_req->arg); 14862306a36Sopenharmony_ci scan_req->scan_result = NULL; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return result; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type, 15562306a36Sopenharmony_ci u8 *ch_freq_list, u8 ch_list_len, 15662306a36Sopenharmony_ci void (*scan_result_fn)(enum scan_event, 15762306a36Sopenharmony_ci struct wilc_rcvd_net_info *, void *), 15862306a36Sopenharmony_ci void *user_arg, struct cfg80211_scan_request *request) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int result = 0; 16162306a36Sopenharmony_ci struct wid wid_list[WILC_SCAN_WID_LIST_SIZE]; 16262306a36Sopenharmony_ci u32 index = 0; 16362306a36Sopenharmony_ci u32 i, scan_timeout; 16462306a36Sopenharmony_ci u8 *buffer; 16562306a36Sopenharmony_ci u8 valuesize = 0; 16662306a36Sopenharmony_ci u8 *search_ssid_vals = NULL; 16762306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (hif_drv->hif_state >= HOST_IF_SCANNING && 17062306a36Sopenharmony_ci hif_drv->hif_state < HOST_IF_CONNECTED) { 17162306a36Sopenharmony_ci netdev_err(vif->ndev, "Already scan\n"); 17262306a36Sopenharmony_ci result = -EBUSY; 17362306a36Sopenharmony_ci goto error; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (vif->connecting) { 17762306a36Sopenharmony_ci netdev_err(vif->ndev, "Don't do obss scan\n"); 17862306a36Sopenharmony_ci result = -EBUSY; 17962306a36Sopenharmony_ci goto error; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci hif_drv->usr_scan_req.ch_cnt = 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (request->n_ssids) { 18562306a36Sopenharmony_ci for (i = 0; i < request->n_ssids; i++) 18662306a36Sopenharmony_ci valuesize += ((request->ssids[i].ssid_len) + 1); 18762306a36Sopenharmony_ci search_ssid_vals = kmalloc(valuesize + 1, GFP_KERNEL); 18862306a36Sopenharmony_ci if (search_ssid_vals) { 18962306a36Sopenharmony_ci wid_list[index].id = WID_SSID_PROBE_REQ; 19062306a36Sopenharmony_ci wid_list[index].type = WID_STR; 19162306a36Sopenharmony_ci wid_list[index].val = search_ssid_vals; 19262306a36Sopenharmony_ci buffer = wid_list[index].val; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci *buffer++ = request->n_ssids; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci for (i = 0; i < request->n_ssids; i++) { 19762306a36Sopenharmony_ci *buffer++ = request->ssids[i].ssid_len; 19862306a36Sopenharmony_ci memcpy(buffer, request->ssids[i].ssid, 19962306a36Sopenharmony_ci request->ssids[i].ssid_len); 20062306a36Sopenharmony_ci buffer += request->ssids[i].ssid_len; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci wid_list[index].size = (s32)(valuesize + 1); 20362306a36Sopenharmony_ci index++; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci wid_list[index].id = WID_INFO_ELEMENT_PROBE; 20862306a36Sopenharmony_ci wid_list[index].type = WID_BIN_DATA; 20962306a36Sopenharmony_ci wid_list[index].val = (s8 *)request->ie; 21062306a36Sopenharmony_ci wid_list[index].size = request->ie_len; 21162306a36Sopenharmony_ci index++; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci wid_list[index].id = WID_SCAN_TYPE; 21462306a36Sopenharmony_ci wid_list[index].type = WID_CHAR; 21562306a36Sopenharmony_ci wid_list[index].size = sizeof(char); 21662306a36Sopenharmony_ci wid_list[index].val = (s8 *)&scan_type; 21762306a36Sopenharmony_ci index++; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (scan_type == WILC_FW_PASSIVE_SCAN && request->duration) { 22062306a36Sopenharmony_ci wid_list[index].id = WID_PASSIVE_SCAN_TIME; 22162306a36Sopenharmony_ci wid_list[index].type = WID_SHORT; 22262306a36Sopenharmony_ci wid_list[index].size = sizeof(u16); 22362306a36Sopenharmony_ci wid_list[index].val = (s8 *)&request->duration; 22462306a36Sopenharmony_ci index++; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci scan_timeout = (request->duration * ch_list_len) + 500; 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci scan_timeout = WILC_HIF_SCAN_TIMEOUT_MS; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci wid_list[index].id = WID_SCAN_CHANNEL_LIST; 23262306a36Sopenharmony_ci wid_list[index].type = WID_BIN_DATA; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (ch_freq_list && ch_list_len > 0) { 23562306a36Sopenharmony_ci for (i = 0; i < ch_list_len; i++) { 23662306a36Sopenharmony_ci if (ch_freq_list[i] > 0) 23762306a36Sopenharmony_ci ch_freq_list[i] -= 1; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci wid_list[index].val = ch_freq_list; 24262306a36Sopenharmony_ci wid_list[index].size = ch_list_len; 24362306a36Sopenharmony_ci index++; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci wid_list[index].id = WID_START_SCAN_REQ; 24662306a36Sopenharmony_ci wid_list[index].type = WID_CHAR; 24762306a36Sopenharmony_ci wid_list[index].size = sizeof(char); 24862306a36Sopenharmony_ci wid_list[index].val = (s8 *)&scan_source; 24962306a36Sopenharmony_ci index++; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci hif_drv->usr_scan_req.scan_result = scan_result_fn; 25262306a36Sopenharmony_ci hif_drv->usr_scan_req.arg = user_arg; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, index); 25562306a36Sopenharmony_ci if (result) { 25662306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send scan parameters\n"); 25762306a36Sopenharmony_ci goto error; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci hif_drv->scan_timer_vif = vif; 26162306a36Sopenharmony_ci mod_timer(&hif_drv->scan_timer, 26262306a36Sopenharmony_ci jiffies + msecs_to_jiffies(scan_timeout)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cierror: 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci kfree(search_ssid_vals); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return result; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int wilc_send_connect_wid(struct wilc_vif *vif) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int result = 0; 27462306a36Sopenharmony_ci struct wid wid_list[5]; 27562306a36Sopenharmony_ci u32 wid_cnt = 0; 27662306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 27762306a36Sopenharmony_ci struct wilc_conn_info *conn_attr = &hif_drv->conn_info; 27862306a36Sopenharmony_ci struct wilc_join_bss_param *bss_param = conn_attr->param; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_SET_MFP; 28262306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_CHAR; 28362306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(char); 28462306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&conn_attr->mfp_type; 28562306a36Sopenharmony_ci wid_cnt++; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_INFO_ELEMENT_ASSOCIATE; 28862306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_BIN_DATA; 28962306a36Sopenharmony_ci wid_list[wid_cnt].val = conn_attr->req_ies; 29062306a36Sopenharmony_ci wid_list[wid_cnt].size = conn_attr->req_ies_len; 29162306a36Sopenharmony_ci wid_cnt++; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_11I_MODE; 29462306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_CHAR; 29562306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(char); 29662306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&conn_attr->security; 29762306a36Sopenharmony_ci wid_cnt++; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_AUTH_TYPE; 30062306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_CHAR; 30162306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(char); 30262306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&conn_attr->auth_type; 30362306a36Sopenharmony_ci wid_cnt++; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_JOIN_REQ_EXTENDED; 30662306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_STR; 30762306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(*bss_param); 30862306a36Sopenharmony_ci wid_list[wid_cnt].val = (u8 *)bss_param; 30962306a36Sopenharmony_ci wid_cnt++; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, wid_cnt); 31262306a36Sopenharmony_ci if (result) { 31362306a36Sopenharmony_ci netdev_err(vif->ndev, "failed to send config packet\n"); 31462306a36Sopenharmony_ci goto error; 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci if (conn_attr->auth_type == WILC_FW_AUTH_SAE) 31762306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_EXTERNAL_AUTH; 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cierror: 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci kfree(conn_attr->req_ies); 32762306a36Sopenharmony_ci conn_attr->req_ies = NULL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return result; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void handle_connect_timeout(struct work_struct *work) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 33562306a36Sopenharmony_ci struct wilc_vif *vif = msg->vif; 33662306a36Sopenharmony_ci int result; 33762306a36Sopenharmony_ci struct wid wid; 33862306a36Sopenharmony_ci u16 dummy_reason_code = 0; 33962306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!hif_drv) { 34262306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__); 34362306a36Sopenharmony_ci goto out; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (hif_drv->conn_info.conn_result) { 34962306a36Sopenharmony_ci hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_CONN_RESP, 35062306a36Sopenharmony_ci WILC_MAC_STATUS_DISCONNECTED, 35162306a36Sopenharmony_ci hif_drv->conn_info.arg); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci wid.id = WID_DISCONNECT; 35862306a36Sopenharmony_ci wid.type = WID_CHAR; 35962306a36Sopenharmony_ci wid.val = (s8 *)&dummy_reason_code; 36062306a36Sopenharmony_ci wid.size = sizeof(char); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 36362306a36Sopenharmony_ci if (result) 36462306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send disconnect\n"); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci hif_drv->conn_info.req_ies_len = 0; 36762306a36Sopenharmony_ci kfree(hif_drv->conn_info.req_ies); 36862306a36Sopenharmony_ci hif_drv->conn_info.req_ies = NULL; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciout: 37162306a36Sopenharmony_ci kfree(msg); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_civoid *wilc_parse_join_bss_param(struct cfg80211_bss *bss, 37562306a36Sopenharmony_ci struct cfg80211_crypto_settings *crypto) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci const u8 *ies_data, *tim_elm, *ssid_elm, *rates_ie, *supp_rates_ie; 37862306a36Sopenharmony_ci const u8 *ht_ie, *wpa_ie, *wmm_ie, *rsn_ie; 37962306a36Sopenharmony_ci struct ieee80211_p2p_noa_attr noa_attr; 38062306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 38162306a36Sopenharmony_ci struct wilc_join_bss_param *param; 38262306a36Sopenharmony_ci u8 rates_len = 0, ies_len; 38362306a36Sopenharmony_ci int ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci param = kzalloc(sizeof(*param), GFP_KERNEL); 38662306a36Sopenharmony_ci if (!param) 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci rcu_read_lock(); 39062306a36Sopenharmony_ci ies = rcu_dereference(bss->ies); 39162306a36Sopenharmony_ci ies_data = kmemdup(ies->data, ies->len, GFP_ATOMIC); 39262306a36Sopenharmony_ci if (!ies_data) { 39362306a36Sopenharmony_ci rcu_read_unlock(); 39462306a36Sopenharmony_ci kfree(param); 39562306a36Sopenharmony_ci return NULL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci ies_len = ies->len; 39862306a36Sopenharmony_ci rcu_read_unlock(); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci param->beacon_period = cpu_to_le16(bss->beacon_interval); 40162306a36Sopenharmony_ci param->cap_info = cpu_to_le16(bss->capability); 40262306a36Sopenharmony_ci param->bss_type = WILC_FW_BSS_TYPE_INFRA; 40362306a36Sopenharmony_ci param->ch = ieee80211_frequency_to_channel(bss->channel->center_freq); 40462306a36Sopenharmony_ci ether_addr_copy(param->bssid, bss->bssid); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ssid_elm = cfg80211_find_ie(WLAN_EID_SSID, ies_data, ies_len); 40762306a36Sopenharmony_ci if (ssid_elm) { 40862306a36Sopenharmony_ci if (ssid_elm[1] <= IEEE80211_MAX_SSID_LEN) 40962306a36Sopenharmony_ci memcpy(param->ssid, ssid_elm + 2, ssid_elm[1]); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci tim_elm = cfg80211_find_ie(WLAN_EID_TIM, ies_data, ies_len); 41362306a36Sopenharmony_ci if (tim_elm && tim_elm[1] >= 2) 41462306a36Sopenharmony_ci param->dtim_period = tim_elm[3]; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci memset(param->p_suites, 0xFF, 3); 41762306a36Sopenharmony_ci memset(param->akm_suites, 0xFF, 3); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci rates_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, ies_data, ies_len); 42062306a36Sopenharmony_ci if (rates_ie) { 42162306a36Sopenharmony_ci rates_len = rates_ie[1]; 42262306a36Sopenharmony_ci if (rates_len > WILC_MAX_RATES_SUPPORTED) 42362306a36Sopenharmony_ci rates_len = WILC_MAX_RATES_SUPPORTED; 42462306a36Sopenharmony_ci param->supp_rates[0] = rates_len; 42562306a36Sopenharmony_ci memcpy(¶m->supp_rates[1], rates_ie + 2, rates_len); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (rates_len < WILC_MAX_RATES_SUPPORTED) { 42962306a36Sopenharmony_ci supp_rates_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, 43062306a36Sopenharmony_ci ies_data, ies_len); 43162306a36Sopenharmony_ci if (supp_rates_ie) { 43262306a36Sopenharmony_ci u8 ext_rates = supp_rates_ie[1]; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (ext_rates > (WILC_MAX_RATES_SUPPORTED - rates_len)) 43562306a36Sopenharmony_ci param->supp_rates[0] = WILC_MAX_RATES_SUPPORTED; 43662306a36Sopenharmony_ci else 43762306a36Sopenharmony_ci param->supp_rates[0] += ext_rates; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci memcpy(¶m->supp_rates[rates_len + 1], 44062306a36Sopenharmony_ci supp_rates_ie + 2, 44162306a36Sopenharmony_ci (param->supp_rates[0] - rates_len)); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies_data, ies_len); 44662306a36Sopenharmony_ci if (ht_ie) 44762306a36Sopenharmony_ci param->ht_capable = true; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = cfg80211_get_p2p_attr(ies_data, ies_len, 45062306a36Sopenharmony_ci IEEE80211_P2P_ATTR_ABSENCE_NOTICE, 45162306a36Sopenharmony_ci (u8 *)&noa_attr, sizeof(noa_attr)); 45262306a36Sopenharmony_ci if (ret > 0) { 45362306a36Sopenharmony_ci param->tsf_lo = cpu_to_le32(ies->tsf); 45462306a36Sopenharmony_ci param->noa_enabled = 1; 45562306a36Sopenharmony_ci param->idx = noa_attr.index; 45662306a36Sopenharmony_ci if (noa_attr.oppps_ctwindow & IEEE80211_P2P_OPPPS_ENABLE_BIT) { 45762306a36Sopenharmony_ci param->opp_enabled = 1; 45862306a36Sopenharmony_ci param->opp_en.ct_window = noa_attr.oppps_ctwindow; 45962306a36Sopenharmony_ci param->opp_en.cnt = noa_attr.desc[0].count; 46062306a36Sopenharmony_ci param->opp_en.duration = noa_attr.desc[0].duration; 46162306a36Sopenharmony_ci param->opp_en.interval = noa_attr.desc[0].interval; 46262306a36Sopenharmony_ci param->opp_en.start_time = noa_attr.desc[0].start_time; 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci param->opp_enabled = 0; 46562306a36Sopenharmony_ci param->opp_dis.cnt = noa_attr.desc[0].count; 46662306a36Sopenharmony_ci param->opp_dis.duration = noa_attr.desc[0].duration; 46762306a36Sopenharmony_ci param->opp_dis.interval = noa_attr.desc[0].interval; 46862306a36Sopenharmony_ci param->opp_dis.start_time = noa_attr.desc[0].start_time; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci wmm_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, 47262306a36Sopenharmony_ci WLAN_OUI_TYPE_MICROSOFT_WMM, 47362306a36Sopenharmony_ci ies_data, ies_len); 47462306a36Sopenharmony_ci if (wmm_ie) { 47562306a36Sopenharmony_ci struct ieee80211_wmm_param_ie *ie; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ie = (struct ieee80211_wmm_param_ie *)wmm_ie; 47862306a36Sopenharmony_ci if ((ie->oui_subtype == 0 || ie->oui_subtype == 1) && 47962306a36Sopenharmony_ci ie->version == 1) { 48062306a36Sopenharmony_ci param->wmm_cap = true; 48162306a36Sopenharmony_ci if (ie->qos_info & BIT(7)) 48262306a36Sopenharmony_ci param->uapsd_cap = true; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci wpa_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, 48762306a36Sopenharmony_ci WLAN_OUI_TYPE_MICROSOFT_WPA, 48862306a36Sopenharmony_ci ies_data, ies_len); 48962306a36Sopenharmony_ci if (wpa_ie) { 49062306a36Sopenharmony_ci param->mode_802_11i = 1; 49162306a36Sopenharmony_ci param->rsn_found = true; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, ies_data, ies_len); 49562306a36Sopenharmony_ci if (rsn_ie) { 49662306a36Sopenharmony_ci int rsn_ie_len = sizeof(struct element) + rsn_ie[1]; 49762306a36Sopenharmony_ci int offset = 8; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci param->mode_802_11i = 2; 50062306a36Sopenharmony_ci param->rsn_found = true; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* extract RSN capabilities */ 50362306a36Sopenharmony_ci if (offset < rsn_ie_len) { 50462306a36Sopenharmony_ci /* skip over pairwise suites */ 50562306a36Sopenharmony_ci offset += (rsn_ie[offset] * 4) + 2; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (offset < rsn_ie_len) { 50862306a36Sopenharmony_ci /* skip over authentication suites */ 50962306a36Sopenharmony_ci offset += (rsn_ie[offset] * 4) + 2; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (offset + 1 < rsn_ie_len) 51262306a36Sopenharmony_ci memcpy(param->rsn_cap, &rsn_ie[offset], 2); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (param->rsn_found) { 51862306a36Sopenharmony_ci int i; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci param->rsn_grp_policy = crypto->cipher_group & 0xFF; 52162306a36Sopenharmony_ci for (i = 0; i < crypto->n_ciphers_pairwise && i < 3; i++) 52262306a36Sopenharmony_ci param->p_suites[i] = crypto->ciphers_pairwise[i] & 0xFF; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for (i = 0; i < crypto->n_akm_suites && i < 3; i++) 52562306a36Sopenharmony_ci param->akm_suites[i] = crypto->akm_suites[i] & 0xFF; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci kfree(ies_data); 52962306a36Sopenharmony_ci return (void *)param; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void handle_rcvd_ntwrk_info(struct work_struct *work) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 53562306a36Sopenharmony_ci struct wilc_rcvd_net_info *rcvd_info = &msg->body.net_info; 53662306a36Sopenharmony_ci struct wilc_user_scan_req *scan_req = &msg->vif->hif_drv->usr_scan_req; 53762306a36Sopenharmony_ci const u8 *ch_elm; 53862306a36Sopenharmony_ci u8 *ies; 53962306a36Sopenharmony_ci int ies_len; 54062306a36Sopenharmony_ci size_t offset; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (ieee80211_is_probe_resp(rcvd_info->mgmt->frame_control)) 54362306a36Sopenharmony_ci offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); 54462306a36Sopenharmony_ci else if (ieee80211_is_beacon(rcvd_info->mgmt->frame_control)) 54562306a36Sopenharmony_ci offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci goto done; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ies = rcvd_info->mgmt->u.beacon.variable; 55062306a36Sopenharmony_ci ies_len = rcvd_info->frame_len - offset; 55162306a36Sopenharmony_ci if (ies_len <= 0) 55262306a36Sopenharmony_ci goto done; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ch_elm = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ies, ies_len); 55562306a36Sopenharmony_ci if (ch_elm && ch_elm[1] > 0) 55662306a36Sopenharmony_ci rcvd_info->ch = ch_elm[2]; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (scan_req->scan_result) 55962306a36Sopenharmony_ci scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND, rcvd_info, 56062306a36Sopenharmony_ci scan_req->arg); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cidone: 56362306a36Sopenharmony_ci kfree(rcvd_info->mgmt); 56462306a36Sopenharmony_ci kfree(msg); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic void host_int_get_assoc_res_info(struct wilc_vif *vif, 56862306a36Sopenharmony_ci u8 *assoc_resp_info, 56962306a36Sopenharmony_ci u32 max_assoc_resp_info_len, 57062306a36Sopenharmony_ci u32 *rcvd_assoc_resp_info_len) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int result; 57362306a36Sopenharmony_ci struct wid wid; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci wid.id = WID_ASSOC_RES_INFO; 57662306a36Sopenharmony_ci wid.type = WID_STR; 57762306a36Sopenharmony_ci wid.val = assoc_resp_info; 57862306a36Sopenharmony_ci wid.size = max_assoc_resp_info_len; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); 58162306a36Sopenharmony_ci if (result) { 58262306a36Sopenharmony_ci *rcvd_assoc_resp_info_len = 0; 58362306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send association response\n"); 58462306a36Sopenharmony_ci return; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci *rcvd_assoc_resp_info_len = wid.size; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len, 59162306a36Sopenharmony_ci struct wilc_conn_info *ret_conn_info) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci u8 *ies; 59462306a36Sopenharmony_ci u16 ies_len; 59562306a36Sopenharmony_ci struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ret_conn_info->status = le16_to_cpu(res->status_code); 59862306a36Sopenharmony_ci if (ret_conn_info->status == WLAN_STATUS_SUCCESS) { 59962306a36Sopenharmony_ci ies = &buffer[sizeof(*res)]; 60062306a36Sopenharmony_ci ies_len = buffer_len - sizeof(*res); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ret_conn_info->resp_ies = kmemdup(ies, ies_len, GFP_KERNEL); 60362306a36Sopenharmony_ci if (!ret_conn_info->resp_ies) 60462306a36Sopenharmony_ci return -ENOMEM; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret_conn_info->resp_ies_len = ies_len; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif, 61362306a36Sopenharmony_ci u8 mac_status) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 61662306a36Sopenharmony_ci struct wilc_conn_info *conn_info = &hif_drv->conn_info; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (mac_status == WILC_MAC_STATUS_CONNECTED) { 61962306a36Sopenharmony_ci u32 assoc_resp_info_len; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci memset(hif_drv->assoc_resp, 0, WILC_MAX_ASSOC_RESP_FRAME_SIZE); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci host_int_get_assoc_res_info(vif, hif_drv->assoc_resp, 62462306a36Sopenharmony_ci WILC_MAX_ASSOC_RESP_FRAME_SIZE, 62562306a36Sopenharmony_ci &assoc_resp_info_len); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (assoc_resp_info_len != 0) { 62862306a36Sopenharmony_ci s32 err = 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci err = wilc_parse_assoc_resp_info(hif_drv->assoc_resp, 63162306a36Sopenharmony_ci assoc_resp_info_len, 63262306a36Sopenharmony_ci conn_info); 63362306a36Sopenharmony_ci if (err) 63462306a36Sopenharmony_ci netdev_err(vif->ndev, 63562306a36Sopenharmony_ci "wilc_parse_assoc_resp_info() returned error %d\n", 63662306a36Sopenharmony_ci err); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci del_timer(&hif_drv->connect_timer); 64162306a36Sopenharmony_ci conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, mac_status, 64262306a36Sopenharmony_ci hif_drv->conn_info.arg); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (mac_status == WILC_MAC_STATUS_CONNECTED && 64562306a36Sopenharmony_ci conn_info->status == WLAN_STATUS_SUCCESS) { 64662306a36Sopenharmony_ci ether_addr_copy(hif_drv->assoc_bssid, conn_info->bssid); 64762306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_CONNECTED; 64862306a36Sopenharmony_ci } else { 64962306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci kfree(conn_info->resp_ies); 65362306a36Sopenharmony_ci conn_info->resp_ies = NULL; 65462306a36Sopenharmony_ci conn_info->resp_ies_len = 0; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci kfree(conn_info->req_ies); 65762306a36Sopenharmony_ci conn_info->req_ies = NULL; 65862306a36Sopenharmony_ci conn_info->req_ies_len = 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid wilc_handle_disconnect(struct wilc_vif *vif) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (hif_drv->usr_scan_req.scan_result) { 66662306a36Sopenharmony_ci del_timer(&hif_drv->scan_timer); 66762306a36Sopenharmony_ci handle_scan_done(vif, SCAN_EVENT_ABORTED); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (hif_drv->conn_info.conn_result) 67162306a36Sopenharmony_ci hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 67262306a36Sopenharmony_ci 0, hif_drv->conn_info.arg); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci eth_zero_addr(hif_drv->assoc_bssid); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci hif_drv->conn_info.req_ies_len = 0; 67762306a36Sopenharmony_ci kfree(hif_drv->conn_info.req_ies); 67862306a36Sopenharmony_ci hif_drv->conn_info.req_ies = NULL; 67962306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void handle_rcvd_gnrl_async_info(struct work_struct *work) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 68562306a36Sopenharmony_ci struct wilc_vif *vif = msg->vif; 68662306a36Sopenharmony_ci struct wilc_rcvd_mac_info *mac_info = &msg->body.mac_info; 68762306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!hif_drv) { 69062306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__); 69162306a36Sopenharmony_ci goto free_msg; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (!hif_drv->conn_info.conn_result) { 69562306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); 69662306a36Sopenharmony_ci goto free_msg; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) { 70162306a36Sopenharmony_ci cfg80211_external_auth_request(vif->ndev, &vif->auth, 70262306a36Sopenharmony_ci GFP_KERNEL); 70362306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP; 70462306a36Sopenharmony_ci } else if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) { 70562306a36Sopenharmony_ci host_int_parse_assoc_resp_info(vif, mac_info->status); 70662306a36Sopenharmony_ci } else if (mac_info->status == WILC_MAC_STATUS_DISCONNECTED) { 70762306a36Sopenharmony_ci if (hif_drv->hif_state == HOST_IF_CONNECTED) { 70862306a36Sopenharmony_ci wilc_handle_disconnect(vif); 70962306a36Sopenharmony_ci } else if (hif_drv->usr_scan_req.scan_result) { 71062306a36Sopenharmony_ci del_timer(&hif_drv->scan_timer); 71162306a36Sopenharmony_ci handle_scan_done(vif, SCAN_EVENT_ABORTED); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cifree_msg: 71662306a36Sopenharmony_ci kfree(msg); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciint wilc_disconnect(struct wilc_vif *vif) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct wid wid; 72262306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 72362306a36Sopenharmony_ci struct wilc_user_scan_req *scan_req; 72462306a36Sopenharmony_ci struct wilc_conn_info *conn_info; 72562306a36Sopenharmony_ci int result; 72662306a36Sopenharmony_ci u16 dummy_reason_code = 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci wid.id = WID_DISCONNECT; 72962306a36Sopenharmony_ci wid.type = WID_CHAR; 73062306a36Sopenharmony_ci wid.val = (s8 *)&dummy_reason_code; 73162306a36Sopenharmony_ci wid.size = sizeof(char); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 73462306a36Sopenharmony_ci if (result) { 73562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send disconnect\n"); 73662306a36Sopenharmony_ci return result; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci scan_req = &hif_drv->usr_scan_req; 74062306a36Sopenharmony_ci conn_info = &hif_drv->conn_info; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (scan_req->scan_result) { 74362306a36Sopenharmony_ci del_timer(&hif_drv->scan_timer); 74462306a36Sopenharmony_ci scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->arg); 74562306a36Sopenharmony_ci scan_req->scan_result = NULL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (conn_info->conn_result) { 74962306a36Sopenharmony_ci if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP || 75062306a36Sopenharmony_ci hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) 75162306a36Sopenharmony_ci del_timer(&hif_drv->connect_timer); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 0, 75462306a36Sopenharmony_ci conn_info->arg); 75562306a36Sopenharmony_ci } else { 75662306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci eth_zero_addr(hif_drv->assoc_bssid); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci conn_info->req_ies_len = 0; 76462306a36Sopenharmony_ci kfree(conn_info->req_ies); 76562306a36Sopenharmony_ci conn_info->req_ies = NULL; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ciint wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct wid wid_list[5]; 77362306a36Sopenharmony_ci u32 wid_cnt = 0, result; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_LINKSPEED; 77662306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_CHAR; 77762306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(char); 77862306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&stats->link_speed; 77962306a36Sopenharmony_ci wid_cnt++; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_RSSI; 78262306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_CHAR; 78362306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(char); 78462306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&stats->rssi; 78562306a36Sopenharmony_ci wid_cnt++; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_SUCCESS_FRAME_COUNT; 78862306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_INT; 78962306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(u32); 79062306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&stats->tx_cnt; 79162306a36Sopenharmony_ci wid_cnt++; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_RECEIVED_FRAGMENT_COUNT; 79462306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_INT; 79562306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(u32); 79662306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&stats->rx_cnt; 79762306a36Sopenharmony_ci wid_cnt++; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci wid_list[wid_cnt].id = WID_FAILED_COUNT; 80062306a36Sopenharmony_ci wid_list[wid_cnt].type = WID_INT; 80162306a36Sopenharmony_ci wid_list[wid_cnt].size = sizeof(u32); 80262306a36Sopenharmony_ci wid_list[wid_cnt].val = (s8 *)&stats->tx_fail_cnt; 80362306a36Sopenharmony_ci wid_cnt++; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_GET_CFG, wid_list, wid_cnt); 80662306a36Sopenharmony_ci if (result) { 80762306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send scan parameters\n"); 80862306a36Sopenharmony_ci return result; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (stats->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH && 81262306a36Sopenharmony_ci stats->link_speed != DEFAULT_LINK_SPEED) 81362306a36Sopenharmony_ci wilc_enable_tcp_ack_filter(vif, true); 81462306a36Sopenharmony_ci else if (stats->link_speed != DEFAULT_LINK_SPEED) 81562306a36Sopenharmony_ci wilc_enable_tcp_ack_filter(vif, false); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return result; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic void handle_get_statistics(struct work_struct *work) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 82362306a36Sopenharmony_ci struct wilc_vif *vif = msg->vif; 82462306a36Sopenharmony_ci struct rf_info *stats = (struct rf_info *)msg->body.data; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci wilc_get_statistics(vif, stats); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci kfree(msg); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic void wilc_hif_pack_sta_param(u8 *cur_byte, const u8 *mac, 83262306a36Sopenharmony_ci struct station_parameters *params) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci ether_addr_copy(cur_byte, mac); 83562306a36Sopenharmony_ci cur_byte += ETH_ALEN; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci put_unaligned_le16(params->aid, cur_byte); 83862306a36Sopenharmony_ci cur_byte += 2; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci *cur_byte++ = params->link_sta_params.supported_rates_len; 84162306a36Sopenharmony_ci if (params->link_sta_params.supported_rates_len > 0) 84262306a36Sopenharmony_ci memcpy(cur_byte, params->link_sta_params.supported_rates, 84362306a36Sopenharmony_ci params->link_sta_params.supported_rates_len); 84462306a36Sopenharmony_ci cur_byte += params->link_sta_params.supported_rates_len; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (params->link_sta_params.ht_capa) { 84762306a36Sopenharmony_ci *cur_byte++ = true; 84862306a36Sopenharmony_ci memcpy(cur_byte, params->link_sta_params.ht_capa, 84962306a36Sopenharmony_ci sizeof(struct ieee80211_ht_cap)); 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci *cur_byte++ = false; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci cur_byte += sizeof(struct ieee80211_ht_cap); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci put_unaligned_le16(params->sta_flags_mask, cur_byte); 85662306a36Sopenharmony_ci cur_byte += 2; 85762306a36Sopenharmony_ci put_unaligned_le16(params->sta_flags_set, cur_byte); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int handle_remain_on_chan(struct wilc_vif *vif, 86162306a36Sopenharmony_ci struct wilc_remain_ch *hif_remain_ch) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci int result; 86462306a36Sopenharmony_ci u8 remain_on_chan_flag; 86562306a36Sopenharmony_ci struct wid wid; 86662306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (hif_drv->usr_scan_req.scan_result) 86962306a36Sopenharmony_ci return -EBUSY; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) 87262306a36Sopenharmony_ci return -EBUSY; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (vif->connecting) 87562306a36Sopenharmony_ci return -EBUSY; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci remain_on_chan_flag = true; 87862306a36Sopenharmony_ci wid.id = WID_REMAIN_ON_CHAN; 87962306a36Sopenharmony_ci wid.type = WID_STR; 88062306a36Sopenharmony_ci wid.size = 2; 88162306a36Sopenharmony_ci wid.val = kmalloc(wid.size, GFP_KERNEL); 88262306a36Sopenharmony_ci if (!wid.val) 88362306a36Sopenharmony_ci return -ENOMEM; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci wid.val[0] = remain_on_chan_flag; 88662306a36Sopenharmony_ci wid.val[1] = (s8)hif_remain_ch->ch; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 88962306a36Sopenharmony_ci kfree(wid.val); 89062306a36Sopenharmony_ci if (result) 89162306a36Sopenharmony_ci return -EBUSY; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci hif_drv->remain_on_ch.arg = hif_remain_ch->arg; 89462306a36Sopenharmony_ci hif_drv->remain_on_ch.expired = hif_remain_ch->expired; 89562306a36Sopenharmony_ci hif_drv->remain_on_ch.ch = hif_remain_ch->ch; 89662306a36Sopenharmony_ci hif_drv->remain_on_ch.cookie = hif_remain_ch->cookie; 89762306a36Sopenharmony_ci hif_drv->remain_on_ch_timer_vif = vif; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int wilc_handle_roc_expired(struct wilc_vif *vif, u64 cookie) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci u8 remain_on_chan_flag; 90562306a36Sopenharmony_ci struct wid wid; 90662306a36Sopenharmony_ci int result; 90762306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (vif->priv.p2p_listen_state) { 91062306a36Sopenharmony_ci remain_on_chan_flag = false; 91162306a36Sopenharmony_ci wid.id = WID_REMAIN_ON_CHAN; 91262306a36Sopenharmony_ci wid.type = WID_STR; 91362306a36Sopenharmony_ci wid.size = 2; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci wid.val = kmalloc(wid.size, GFP_KERNEL); 91662306a36Sopenharmony_ci if (!wid.val) 91762306a36Sopenharmony_ci return -ENOMEM; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci wid.val[0] = remain_on_chan_flag; 92062306a36Sopenharmony_ci wid.val[1] = WILC_FALSE_FRMWR_CHANNEL; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 92362306a36Sopenharmony_ci kfree(wid.val); 92462306a36Sopenharmony_ci if (result != 0) { 92562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set remain channel\n"); 92662306a36Sopenharmony_ci return -EINVAL; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (hif_drv->remain_on_ch.expired) { 93062306a36Sopenharmony_ci hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg, 93162306a36Sopenharmony_ci cookie); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci } else { 93462306a36Sopenharmony_ci netdev_dbg(vif->ndev, "Not in listen state\n"); 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return 0; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void wilc_handle_listen_state_expired(struct work_struct *work) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci wilc_handle_roc_expired(msg->vif, msg->body.remain_on_ch.cookie); 94562306a36Sopenharmony_ci kfree(msg); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic void listen_timer_cb(struct timer_list *t) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct host_if_drv *hif_drv = from_timer(hif_drv, t, 95162306a36Sopenharmony_ci remain_on_ch_timer); 95262306a36Sopenharmony_ci struct wilc_vif *vif = hif_drv->remain_on_ch_timer_vif; 95362306a36Sopenharmony_ci int result; 95462306a36Sopenharmony_ci struct host_if_msg *msg; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci del_timer(&vif->hif_drv->remain_on_ch_timer); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci msg = wilc_alloc_work(vif, wilc_handle_listen_state_expired, false); 95962306a36Sopenharmony_ci if (IS_ERR(msg)) 96062306a36Sopenharmony_ci return; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci msg->body.remain_on_ch.cookie = vif->hif_drv->remain_on_ch.cookie; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 96562306a36Sopenharmony_ci if (result) { 96662306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); 96762306a36Sopenharmony_ci kfree(msg); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic void handle_set_mcast_filter(struct work_struct *work) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 97462306a36Sopenharmony_ci struct wilc_vif *vif = msg->vif; 97562306a36Sopenharmony_ci struct wilc_set_multicast *set_mc = &msg->body.mc_info; 97662306a36Sopenharmony_ci int result; 97762306a36Sopenharmony_ci struct wid wid; 97862306a36Sopenharmony_ci u8 *cur_byte; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci wid.id = WID_SETUP_MULTICAST_FILTER; 98162306a36Sopenharmony_ci wid.type = WID_BIN; 98262306a36Sopenharmony_ci wid.size = sizeof(struct wilc_set_multicast) + (set_mc->cnt * ETH_ALEN); 98362306a36Sopenharmony_ci wid.val = kmalloc(wid.size, GFP_KERNEL); 98462306a36Sopenharmony_ci if (!wid.val) 98562306a36Sopenharmony_ci goto error; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci cur_byte = wid.val; 98862306a36Sopenharmony_ci put_unaligned_le32(set_mc->enabled, cur_byte); 98962306a36Sopenharmony_ci cur_byte += 4; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci put_unaligned_le32(set_mc->cnt, cur_byte); 99262306a36Sopenharmony_ci cur_byte += 4; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (set_mc->cnt > 0 && set_mc->mc_list) 99562306a36Sopenharmony_ci memcpy(cur_byte, set_mc->mc_list, set_mc->cnt * ETH_ALEN); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 99862306a36Sopenharmony_ci if (result) 99962306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send setup multicast\n"); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cierror: 100262306a36Sopenharmony_ci kfree(set_mc->mc_list); 100362306a36Sopenharmony_ci kfree(wid.val); 100462306a36Sopenharmony_ci kfree(msg); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_civoid wilc_set_wowlan_trigger(struct wilc_vif *vif, bool enabled) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci int ret; 101062306a36Sopenharmony_ci struct wid wid; 101162306a36Sopenharmony_ci u8 wowlan_trigger = 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (enabled) 101462306a36Sopenharmony_ci wowlan_trigger = 1; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci wid.id = WID_WOWLAN_TRIGGER; 101762306a36Sopenharmony_ci wid.type = WID_CHAR; 101862306a36Sopenharmony_ci wid.val = &wowlan_trigger; 101962306a36Sopenharmony_ci wid.size = sizeof(char); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 102262306a36Sopenharmony_ci if (ret) 102362306a36Sopenharmony_ci pr_err("Failed to send wowlan trigger config packet\n"); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ciint wilc_set_external_auth_param(struct wilc_vif *vif, 102762306a36Sopenharmony_ci struct cfg80211_external_auth_params *auth) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci int ret; 103062306a36Sopenharmony_ci struct wid wid; 103162306a36Sopenharmony_ci struct wilc_external_auth_param *param; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci wid.id = WID_EXTERNAL_AUTH_PARAM; 103462306a36Sopenharmony_ci wid.type = WID_BIN_DATA; 103562306a36Sopenharmony_ci wid.size = sizeof(*param); 103662306a36Sopenharmony_ci param = kzalloc(sizeof(*param), GFP_KERNEL); 103762306a36Sopenharmony_ci if (!param) 103862306a36Sopenharmony_ci return -EINVAL; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci wid.val = (u8 *)param; 104162306a36Sopenharmony_ci param->action = auth->action; 104262306a36Sopenharmony_ci ether_addr_copy(param->bssid, auth->bssid); 104362306a36Sopenharmony_ci memcpy(param->ssid, auth->ssid.ssid, auth->ssid.ssid_len); 104462306a36Sopenharmony_ci param->ssid_len = auth->ssid.ssid_len; 104562306a36Sopenharmony_ci ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci kfree(param); 104862306a36Sopenharmony_ci return ret; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic void handle_scan_timer(struct work_struct *work) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci handle_scan_done(msg->vif, SCAN_EVENT_ABORTED); 105662306a36Sopenharmony_ci kfree(msg); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic void handle_scan_complete(struct work_struct *work) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct host_if_msg *msg = container_of(work, struct host_if_msg, work); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci del_timer(&msg->vif->hif_drv->scan_timer); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci handle_scan_done(msg->vif, SCAN_EVENT_DONE); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci kfree(msg); 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_cistatic void timer_scan_cb(struct timer_list *t) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct host_if_drv *hif_drv = from_timer(hif_drv, t, scan_timer); 107362306a36Sopenharmony_ci struct wilc_vif *vif = hif_drv->scan_timer_vif; 107462306a36Sopenharmony_ci struct host_if_msg *msg; 107562306a36Sopenharmony_ci int result; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_scan_timer, false); 107862306a36Sopenharmony_ci if (IS_ERR(msg)) 107962306a36Sopenharmony_ci return; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 108262306a36Sopenharmony_ci if (result) 108362306a36Sopenharmony_ci kfree(msg); 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic void timer_connect_cb(struct timer_list *t) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct host_if_drv *hif_drv = from_timer(hif_drv, t, 108962306a36Sopenharmony_ci connect_timer); 109062306a36Sopenharmony_ci struct wilc_vif *vif = hif_drv->connect_timer_vif; 109162306a36Sopenharmony_ci struct host_if_msg *msg; 109262306a36Sopenharmony_ci int result; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_connect_timeout, false); 109562306a36Sopenharmony_ci if (IS_ERR(msg)) 109662306a36Sopenharmony_ci return; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 109962306a36Sopenharmony_ci if (result) 110062306a36Sopenharmony_ci kfree(msg); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ciint wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len, 110462306a36Sopenharmony_ci const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic, 110562306a36Sopenharmony_ci u8 mode, u8 cipher_mode, u8 index) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci int result = 0; 110862306a36Sopenharmony_ci u8 t_key_len = ptk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (mode == WILC_AP_MODE) { 111162306a36Sopenharmony_ci struct wid wid_list[2]; 111262306a36Sopenharmony_ci struct wilc_ap_wpa_ptk *key_buf; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci wid_list[0].id = WID_11I_MODE; 111562306a36Sopenharmony_ci wid_list[0].type = WID_CHAR; 111662306a36Sopenharmony_ci wid_list[0].size = sizeof(char); 111762306a36Sopenharmony_ci wid_list[0].val = (s8 *)&cipher_mode; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); 112062306a36Sopenharmony_ci if (!key_buf) 112162306a36Sopenharmony_ci return -ENOMEM; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci ether_addr_copy(key_buf->mac_addr, mac_addr); 112462306a36Sopenharmony_ci key_buf->index = index; 112562306a36Sopenharmony_ci key_buf->key_len = t_key_len; 112662306a36Sopenharmony_ci memcpy(&key_buf->key[0], ptk, ptk_key_len); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (rx_mic) 112962306a36Sopenharmony_ci memcpy(&key_buf->key[ptk_key_len], rx_mic, 113062306a36Sopenharmony_ci WILC_RX_MIC_KEY_LEN); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (tx_mic) 113362306a36Sopenharmony_ci memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN], 113462306a36Sopenharmony_ci tx_mic, WILC_TX_MIC_KEY_LEN); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci wid_list[1].id = WID_ADD_PTK; 113762306a36Sopenharmony_ci wid_list[1].type = WID_STR; 113862306a36Sopenharmony_ci wid_list[1].size = sizeof(*key_buf) + t_key_len; 113962306a36Sopenharmony_ci wid_list[1].val = (u8 *)key_buf; 114062306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, 114162306a36Sopenharmony_ci ARRAY_SIZE(wid_list)); 114262306a36Sopenharmony_ci kfree(key_buf); 114362306a36Sopenharmony_ci } else if (mode == WILC_STATION_MODE) { 114462306a36Sopenharmony_ci struct wid wid; 114562306a36Sopenharmony_ci struct wilc_sta_wpa_ptk *key_buf; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); 114862306a36Sopenharmony_ci if (!key_buf) 114962306a36Sopenharmony_ci return -ENOMEM; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ether_addr_copy(key_buf->mac_addr, mac_addr); 115262306a36Sopenharmony_ci key_buf->key_len = t_key_len; 115362306a36Sopenharmony_ci memcpy(&key_buf->key[0], ptk, ptk_key_len); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (rx_mic) 115662306a36Sopenharmony_ci memcpy(&key_buf->key[ptk_key_len], rx_mic, 115762306a36Sopenharmony_ci WILC_RX_MIC_KEY_LEN); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (tx_mic) 116062306a36Sopenharmony_ci memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN], 116162306a36Sopenharmony_ci tx_mic, WILC_TX_MIC_KEY_LEN); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci wid.id = WID_ADD_PTK; 116462306a36Sopenharmony_ci wid.type = WID_STR; 116562306a36Sopenharmony_ci wid.size = sizeof(*key_buf) + t_key_len; 116662306a36Sopenharmony_ci wid.val = (s8 *)key_buf; 116762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 116862306a36Sopenharmony_ci kfree(key_buf); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return result; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ciint wilc_add_igtk(struct wilc_vif *vif, const u8 *igtk, u8 igtk_key_len, 117562306a36Sopenharmony_ci const u8 *pn, u8 pn_len, const u8 *mac_addr, u8 mode, u8 index) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci int result = 0; 117862306a36Sopenharmony_ci u8 t_key_len = igtk_key_len; 117962306a36Sopenharmony_ci struct wid wid; 118062306a36Sopenharmony_ci struct wilc_wpa_igtk *key_buf; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); 118362306a36Sopenharmony_ci if (!key_buf) 118462306a36Sopenharmony_ci return -ENOMEM; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci key_buf->index = index; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci memcpy(&key_buf->pn[0], pn, pn_len); 118962306a36Sopenharmony_ci key_buf->pn_len = pn_len; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci memcpy(&key_buf->key[0], igtk, igtk_key_len); 119262306a36Sopenharmony_ci key_buf->key_len = t_key_len; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci wid.id = WID_ADD_IGTK; 119562306a36Sopenharmony_ci wid.type = WID_STR; 119662306a36Sopenharmony_ci wid.size = sizeof(*key_buf) + t_key_len; 119762306a36Sopenharmony_ci wid.val = (s8 *)key_buf; 119862306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 119962306a36Sopenharmony_ci kfree(key_buf); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return result; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciint wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len, 120562306a36Sopenharmony_ci u8 index, u32 key_rsc_len, const u8 *key_rsc, 120662306a36Sopenharmony_ci const u8 *rx_mic, const u8 *tx_mic, u8 mode, 120762306a36Sopenharmony_ci u8 cipher_mode) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci int result = 0; 121062306a36Sopenharmony_ci struct wilc_gtk_key *gtk_key; 121162306a36Sopenharmony_ci int t_key_len = gtk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci gtk_key = kzalloc(sizeof(*gtk_key) + t_key_len, GFP_KERNEL); 121462306a36Sopenharmony_ci if (!gtk_key) 121562306a36Sopenharmony_ci return -ENOMEM; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci /* fill bssid value only in station mode */ 121862306a36Sopenharmony_ci if (mode == WILC_STATION_MODE && 121962306a36Sopenharmony_ci vif->hif_drv->hif_state == HOST_IF_CONNECTED) 122062306a36Sopenharmony_ci memcpy(gtk_key->mac_addr, vif->hif_drv->assoc_bssid, ETH_ALEN); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (key_rsc) 122362306a36Sopenharmony_ci memcpy(gtk_key->rsc, key_rsc, 8); 122462306a36Sopenharmony_ci gtk_key->index = index; 122562306a36Sopenharmony_ci gtk_key->key_len = t_key_len; 122662306a36Sopenharmony_ci memcpy(>k_key->key[0], rx_gtk, gtk_key_len); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (rx_mic) 122962306a36Sopenharmony_ci memcpy(>k_key->key[gtk_key_len], rx_mic, WILC_RX_MIC_KEY_LEN); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (tx_mic) 123262306a36Sopenharmony_ci memcpy(>k_key->key[gtk_key_len + WILC_RX_MIC_KEY_LEN], 123362306a36Sopenharmony_ci tx_mic, WILC_TX_MIC_KEY_LEN); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (mode == WILC_AP_MODE) { 123662306a36Sopenharmony_ci struct wid wid_list[2]; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci wid_list[0].id = WID_11I_MODE; 123962306a36Sopenharmony_ci wid_list[0].type = WID_CHAR; 124062306a36Sopenharmony_ci wid_list[0].size = sizeof(char); 124162306a36Sopenharmony_ci wid_list[0].val = (s8 *)&cipher_mode; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci wid_list[1].id = WID_ADD_RX_GTK; 124462306a36Sopenharmony_ci wid_list[1].type = WID_STR; 124562306a36Sopenharmony_ci wid_list[1].size = sizeof(*gtk_key) + t_key_len; 124662306a36Sopenharmony_ci wid_list[1].val = (u8 *)gtk_key; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, 124962306a36Sopenharmony_ci ARRAY_SIZE(wid_list)); 125062306a36Sopenharmony_ci } else if (mode == WILC_STATION_MODE) { 125162306a36Sopenharmony_ci struct wid wid; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci wid.id = WID_ADD_RX_GTK; 125462306a36Sopenharmony_ci wid.type = WID_STR; 125562306a36Sopenharmony_ci wid.size = sizeof(*gtk_key) + t_key_len; 125662306a36Sopenharmony_ci wid.val = (u8 *)gtk_key; 125762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci kfree(gtk_key); 126162306a36Sopenharmony_ci return result; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ciint wilc_set_pmkid_info(struct wilc_vif *vif, struct wilc_pmkid_attr *pmkid) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci struct wid wid; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci wid.id = WID_PMKID_INFO; 126962306a36Sopenharmony_ci wid.type = WID_STR; 127062306a36Sopenharmony_ci wid.size = (pmkid->numpmkid * sizeof(struct wilc_pmkid)) + 1; 127162306a36Sopenharmony_ci wid.val = (u8 *)pmkid; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ciint wilc_get_mac_address(struct wilc_vif *vif, u8 *mac_addr) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci int result; 127962306a36Sopenharmony_ci struct wid wid; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci wid.id = WID_MAC_ADDR; 128262306a36Sopenharmony_ci wid.type = WID_STR; 128362306a36Sopenharmony_ci wid.size = ETH_ALEN; 128462306a36Sopenharmony_ci wid.val = mac_addr; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); 128762306a36Sopenharmony_ci if (result) 128862306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to get mac address\n"); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci return result; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ciint wilc_set_mac_address(struct wilc_vif *vif, u8 *mac_addr) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct wid wid; 129662306a36Sopenharmony_ci int result; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci wid.id = WID_MAC_ADDR; 129962306a36Sopenharmony_ci wid.type = WID_STR; 130062306a36Sopenharmony_ci wid.size = ETH_ALEN; 130162306a36Sopenharmony_ci wid.val = mac_addr; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 130462306a36Sopenharmony_ci if (result) 130562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set mac address\n"); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return result; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ciint wilc_set_join_req(struct wilc_vif *vif, u8 *bssid, const u8 *ies, 131162306a36Sopenharmony_ci size_t ies_len) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci int result; 131462306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 131562306a36Sopenharmony_ci struct wilc_conn_info *conn_info = &hif_drv->conn_info; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (bssid) 131862306a36Sopenharmony_ci ether_addr_copy(conn_info->bssid, bssid); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (ies) { 132162306a36Sopenharmony_ci conn_info->req_ies_len = ies_len; 132262306a36Sopenharmony_ci conn_info->req_ies = kmemdup(ies, ies_len, GFP_KERNEL); 132362306a36Sopenharmony_ci if (!conn_info->req_ies) 132462306a36Sopenharmony_ci return -ENOMEM; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci result = wilc_send_connect_wid(vif); 132862306a36Sopenharmony_ci if (result) 132962306a36Sopenharmony_ci goto free_ies; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci hif_drv->connect_timer_vif = vif; 133262306a36Sopenharmony_ci mod_timer(&hif_drv->connect_timer, 133362306a36Sopenharmony_ci jiffies + msecs_to_jiffies(WILC_HIF_CONNECT_TIMEOUT_MS)); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci return 0; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cifree_ies: 133862306a36Sopenharmony_ci kfree(conn_info->req_ies); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return result; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ciint wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci struct wid wid; 134662306a36Sopenharmony_ci int result; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci wid.id = WID_CURRENT_CHANNEL; 134962306a36Sopenharmony_ci wid.type = WID_CHAR; 135062306a36Sopenharmony_ci wid.size = sizeof(char); 135162306a36Sopenharmony_ci wid.val = &channel; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 135462306a36Sopenharmony_ci if (result) 135562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set channel\n"); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return result; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ciint wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode, 136162306a36Sopenharmony_ci u8 ifc_id) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci struct wid wid; 136462306a36Sopenharmony_ci int result; 136562306a36Sopenharmony_ci struct wilc_drv_handler drv; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci wid.id = WID_SET_OPERATION_MODE; 136862306a36Sopenharmony_ci wid.type = WID_STR; 136962306a36Sopenharmony_ci wid.size = sizeof(drv); 137062306a36Sopenharmony_ci wid.val = (u8 *)&drv; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci drv.handler = cpu_to_le32(index); 137362306a36Sopenharmony_ci drv.mode = (ifc_id | (mode << 1)); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 137662306a36Sopenharmony_ci if (result) 137762306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set driver handler\n"); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci return result; 138062306a36Sopenharmony_ci} 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cis32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac, u32 *out_val) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci struct wid wid; 138562306a36Sopenharmony_ci s32 result; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci wid.id = WID_SET_STA_MAC_INACTIVE_TIME; 138862306a36Sopenharmony_ci wid.type = WID_STR; 138962306a36Sopenharmony_ci wid.size = ETH_ALEN; 139062306a36Sopenharmony_ci wid.val = kzalloc(wid.size, GFP_KERNEL); 139162306a36Sopenharmony_ci if (!wid.val) 139262306a36Sopenharmony_ci return -ENOMEM; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci ether_addr_copy(wid.val, mac); 139562306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 139662306a36Sopenharmony_ci kfree(wid.val); 139762306a36Sopenharmony_ci if (result) { 139862306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to set inactive mac\n"); 139962306a36Sopenharmony_ci return result; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci wid.id = WID_GET_INACTIVE_TIME; 140362306a36Sopenharmony_ci wid.type = WID_INT; 140462306a36Sopenharmony_ci wid.val = (s8 *)out_val; 140562306a36Sopenharmony_ci wid.size = sizeof(u32); 140662306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); 140762306a36Sopenharmony_ci if (result) 140862306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to get inactive time\n"); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return result; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ciint wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct wid wid; 141662306a36Sopenharmony_ci int result; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (!rssi_level) { 141962306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: RSSI level is NULL\n", __func__); 142062306a36Sopenharmony_ci return -EFAULT; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci wid.id = WID_RSSI; 142462306a36Sopenharmony_ci wid.type = WID_CHAR; 142562306a36Sopenharmony_ci wid.size = sizeof(char); 142662306a36Sopenharmony_ci wid.val = rssi_level; 142762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); 142862306a36Sopenharmony_ci if (result) 142962306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to get RSSI value\n"); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci return result; 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic int wilc_get_stats_async(struct wilc_vif *vif, struct rf_info *stats) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci int result; 143762306a36Sopenharmony_ci struct host_if_msg *msg; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_get_statistics, false); 144062306a36Sopenharmony_ci if (IS_ERR(msg)) 144162306a36Sopenharmony_ci return PTR_ERR(msg); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci msg->body.data = (char *)stats; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 144662306a36Sopenharmony_ci if (result) { 144762306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); 144862306a36Sopenharmony_ci kfree(msg); 144962306a36Sopenharmony_ci return result; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci return result; 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ciint wilc_hif_set_cfg(struct wilc_vif *vif, struct cfg_param_attr *param) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct wid wid_list[4]; 145862306a36Sopenharmony_ci int i = 0; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (param->flag & WILC_CFG_PARAM_RETRY_SHORT) { 146162306a36Sopenharmony_ci wid_list[i].id = WID_SHORT_RETRY_LIMIT; 146262306a36Sopenharmony_ci wid_list[i].val = (s8 *)¶m->short_retry_limit; 146362306a36Sopenharmony_ci wid_list[i].type = WID_SHORT; 146462306a36Sopenharmony_ci wid_list[i].size = sizeof(u16); 146562306a36Sopenharmony_ci i++; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci if (param->flag & WILC_CFG_PARAM_RETRY_LONG) { 146862306a36Sopenharmony_ci wid_list[i].id = WID_LONG_RETRY_LIMIT; 146962306a36Sopenharmony_ci wid_list[i].val = (s8 *)¶m->long_retry_limit; 147062306a36Sopenharmony_ci wid_list[i].type = WID_SHORT; 147162306a36Sopenharmony_ci wid_list[i].size = sizeof(u16); 147262306a36Sopenharmony_ci i++; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci if (param->flag & WILC_CFG_PARAM_FRAG_THRESHOLD) { 147562306a36Sopenharmony_ci wid_list[i].id = WID_FRAG_THRESHOLD; 147662306a36Sopenharmony_ci wid_list[i].val = (s8 *)¶m->frag_threshold; 147762306a36Sopenharmony_ci wid_list[i].type = WID_SHORT; 147862306a36Sopenharmony_ci wid_list[i].size = sizeof(u16); 147962306a36Sopenharmony_ci i++; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci if (param->flag & WILC_CFG_PARAM_RTS_THRESHOLD) { 148262306a36Sopenharmony_ci wid_list[i].id = WID_RTS_THRESHOLD; 148362306a36Sopenharmony_ci wid_list[i].val = (s8 *)¶m->rts_threshold; 148462306a36Sopenharmony_ci wid_list[i].type = WID_SHORT; 148562306a36Sopenharmony_ci wid_list[i].size = sizeof(u16); 148662306a36Sopenharmony_ci i++; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci return wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, i); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_cistatic void get_periodic_rssi(struct timer_list *t) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct wilc_vif *vif = from_timer(vif, t, periodic_rssi); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (!vif->hif_drv) { 149762306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL", __func__); 149862306a36Sopenharmony_ci return; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (vif->hif_drv->hif_state == HOST_IF_CONNECTED) 150262306a36Sopenharmony_ci wilc_get_stats_async(vif, &vif->periodic_stat); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci mod_timer(&vif->periodic_rssi, jiffies + msecs_to_jiffies(5000)); 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ciint wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct host_if_drv *hif_drv; 151062306a36Sopenharmony_ci struct wilc_vif *vif = netdev_priv(dev); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci hif_drv = kzalloc(sizeof(*hif_drv), GFP_KERNEL); 151362306a36Sopenharmony_ci if (!hif_drv) 151462306a36Sopenharmony_ci return -ENOMEM; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci *hif_drv_handler = hif_drv; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci vif->hif_drv = hif_drv; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci timer_setup(&vif->periodic_rssi, get_periodic_rssi, 0); 152162306a36Sopenharmony_ci mod_timer(&vif->periodic_rssi, jiffies + msecs_to_jiffies(5000)); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci timer_setup(&hif_drv->scan_timer, timer_scan_cb, 0); 152462306a36Sopenharmony_ci timer_setup(&hif_drv->connect_timer, timer_connect_cb, 0); 152562306a36Sopenharmony_ci timer_setup(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci hif_drv->p2p_timeout = 0; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci return 0; 153262306a36Sopenharmony_ci} 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ciint wilc_deinit(struct wilc_vif *vif) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci int result = 0; 153762306a36Sopenharmony_ci struct host_if_drv *hif_drv = vif->hif_drv; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (!hif_drv) { 154062306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL", __func__); 154162306a36Sopenharmony_ci return -EFAULT; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci mutex_lock(&vif->wilc->deinit_lock); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci timer_shutdown_sync(&hif_drv->scan_timer); 154762306a36Sopenharmony_ci timer_shutdown_sync(&hif_drv->connect_timer); 154862306a36Sopenharmony_ci del_timer_sync(&vif->periodic_rssi); 154962306a36Sopenharmony_ci timer_shutdown_sync(&hif_drv->remain_on_ch_timer); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci if (hif_drv->usr_scan_req.scan_result) { 155262306a36Sopenharmony_ci hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL, 155362306a36Sopenharmony_ci hif_drv->usr_scan_req.arg); 155462306a36Sopenharmony_ci hif_drv->usr_scan_req.scan_result = NULL; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci hif_drv->hif_state = HOST_IF_IDLE; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci kfree(hif_drv); 156062306a36Sopenharmony_ci vif->hif_drv = NULL; 156162306a36Sopenharmony_ci mutex_unlock(&vif->wilc->deinit_lock); 156262306a36Sopenharmony_ci return result; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_civoid wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci int result; 156862306a36Sopenharmony_ci struct host_if_msg *msg; 156962306a36Sopenharmony_ci int id; 157062306a36Sopenharmony_ci struct host_if_drv *hif_drv; 157162306a36Sopenharmony_ci struct wilc_vif *vif; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci id = get_unaligned_le32(&buffer[length - 4]); 157462306a36Sopenharmony_ci vif = wilc_get_vif_from_idx(wilc, id); 157562306a36Sopenharmony_ci if (!vif) 157662306a36Sopenharmony_ci return; 157762306a36Sopenharmony_ci hif_drv = vif->hif_drv; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (!hif_drv) { 158062306a36Sopenharmony_ci netdev_err(vif->ndev, "driver not init[%p]\n", hif_drv); 158162306a36Sopenharmony_ci return; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false); 158562306a36Sopenharmony_ci if (IS_ERR(msg)) 158662306a36Sopenharmony_ci return; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1; 158962306a36Sopenharmony_ci msg->body.net_info.rssi = buffer[8]; 159062306a36Sopenharmony_ci msg->body.net_info.mgmt = kmemdup(&buffer[9], 159162306a36Sopenharmony_ci msg->body.net_info.frame_len, 159262306a36Sopenharmony_ci GFP_KERNEL); 159362306a36Sopenharmony_ci if (!msg->body.net_info.mgmt) { 159462306a36Sopenharmony_ci kfree(msg); 159562306a36Sopenharmony_ci return; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 159962306a36Sopenharmony_ci if (result) { 160062306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); 160162306a36Sopenharmony_ci kfree(msg->body.net_info.mgmt); 160262306a36Sopenharmony_ci kfree(msg); 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_civoid wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci int result; 160962306a36Sopenharmony_ci struct host_if_msg *msg; 161062306a36Sopenharmony_ci int id; 161162306a36Sopenharmony_ci struct host_if_drv *hif_drv; 161262306a36Sopenharmony_ci struct wilc_vif *vif; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci mutex_lock(&wilc->deinit_lock); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci id = get_unaligned_le32(&buffer[length - 4]); 161762306a36Sopenharmony_ci vif = wilc_get_vif_from_idx(wilc, id); 161862306a36Sopenharmony_ci if (!vif) { 161962306a36Sopenharmony_ci mutex_unlock(&wilc->deinit_lock); 162062306a36Sopenharmony_ci return; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci hif_drv = vif->hif_drv; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (!hif_drv) { 162662306a36Sopenharmony_ci mutex_unlock(&wilc->deinit_lock); 162762306a36Sopenharmony_ci return; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!hif_drv->conn_info.conn_result) { 163162306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); 163262306a36Sopenharmony_ci mutex_unlock(&wilc->deinit_lock); 163362306a36Sopenharmony_ci return; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false); 163762306a36Sopenharmony_ci if (IS_ERR(msg)) { 163862306a36Sopenharmony_ci mutex_unlock(&wilc->deinit_lock); 163962306a36Sopenharmony_ci return; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci msg->body.mac_info.status = buffer[7]; 164362306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 164462306a36Sopenharmony_ci if (result) { 164562306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); 164662306a36Sopenharmony_ci kfree(msg); 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci mutex_unlock(&wilc->deinit_lock); 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_civoid wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci int result; 165562306a36Sopenharmony_ci int id; 165662306a36Sopenharmony_ci struct host_if_drv *hif_drv; 165762306a36Sopenharmony_ci struct wilc_vif *vif; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci id = get_unaligned_le32(&buffer[length - 4]); 166062306a36Sopenharmony_ci vif = wilc_get_vif_from_idx(wilc, id); 166162306a36Sopenharmony_ci if (!vif) 166262306a36Sopenharmony_ci return; 166362306a36Sopenharmony_ci hif_drv = vif->hif_drv; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (!hif_drv) 166662306a36Sopenharmony_ci return; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (hif_drv->usr_scan_req.scan_result) { 166962306a36Sopenharmony_ci struct host_if_msg *msg; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_scan_complete, false); 167262306a36Sopenharmony_ci if (IS_ERR(msg)) 167362306a36Sopenharmony_ci return; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 167662306a36Sopenharmony_ci if (result) { 167762306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", 167862306a36Sopenharmony_ci __func__); 167962306a36Sopenharmony_ci kfree(msg); 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ciint wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, 168562306a36Sopenharmony_ci u32 duration, u16 chan, 168662306a36Sopenharmony_ci void (*expired)(void *, u64), 168762306a36Sopenharmony_ci void *user_arg) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci struct wilc_remain_ch roc; 169062306a36Sopenharmony_ci int result; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci roc.ch = chan; 169362306a36Sopenharmony_ci roc.expired = expired; 169462306a36Sopenharmony_ci roc.arg = user_arg; 169562306a36Sopenharmony_ci roc.duration = duration; 169662306a36Sopenharmony_ci roc.cookie = cookie; 169762306a36Sopenharmony_ci result = handle_remain_on_chan(vif, &roc); 169862306a36Sopenharmony_ci if (result) 169962306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: failed to set remain on channel\n", 170062306a36Sopenharmony_ci __func__); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci return result; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ciint wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci if (!vif->hif_drv) { 170862306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: hif driver is NULL", __func__); 170962306a36Sopenharmony_ci return -EFAULT; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci del_timer(&vif->hif_drv->remain_on_ch_timer); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci return wilc_handle_roc_expired(vif, cookie); 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_civoid wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci struct wid wid; 172062306a36Sopenharmony_ci int result; 172162306a36Sopenharmony_ci struct wilc_reg_frame reg_frame; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci wid.id = WID_REGISTER_FRAME; 172462306a36Sopenharmony_ci wid.type = WID_STR; 172562306a36Sopenharmony_ci wid.size = sizeof(reg_frame); 172662306a36Sopenharmony_ci wid.val = (u8 *)®_frame; 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci memset(®_frame, 0x0, sizeof(reg_frame)); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci if (reg) 173162306a36Sopenharmony_ci reg_frame.reg = 1; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci switch (frame_type) { 173462306a36Sopenharmony_ci case IEEE80211_STYPE_ACTION: 173562306a36Sopenharmony_ci reg_frame.reg_id = WILC_FW_ACTION_FRM_IDX; 173662306a36Sopenharmony_ci break; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci case IEEE80211_STYPE_PROBE_REQ: 173962306a36Sopenharmony_ci reg_frame.reg_id = WILC_FW_PROBE_REQ_IDX; 174062306a36Sopenharmony_ci break; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci case IEEE80211_STYPE_AUTH: 174362306a36Sopenharmony_ci reg_frame.reg_id = WILC_FW_AUTH_REQ_IDX; 174462306a36Sopenharmony_ci break; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci default: 174762306a36Sopenharmony_ci break; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci reg_frame.frame_type = cpu_to_le16(frame_type); 175062306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 175162306a36Sopenharmony_ci if (result) 175262306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to frame register\n"); 175362306a36Sopenharmony_ci} 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ciint wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period, 175662306a36Sopenharmony_ci struct cfg80211_beacon_data *params) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct wid wid; 175962306a36Sopenharmony_ci int result; 176062306a36Sopenharmony_ci u8 *cur_byte; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci wid.id = WID_ADD_BEACON; 176362306a36Sopenharmony_ci wid.type = WID_BIN; 176462306a36Sopenharmony_ci wid.size = params->head_len + params->tail_len + 16; 176562306a36Sopenharmony_ci wid.val = kzalloc(wid.size, GFP_KERNEL); 176662306a36Sopenharmony_ci if (!wid.val) 176762306a36Sopenharmony_ci return -ENOMEM; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci cur_byte = wid.val; 177062306a36Sopenharmony_ci put_unaligned_le32(interval, cur_byte); 177162306a36Sopenharmony_ci cur_byte += 4; 177262306a36Sopenharmony_ci put_unaligned_le32(dtim_period, cur_byte); 177362306a36Sopenharmony_ci cur_byte += 4; 177462306a36Sopenharmony_ci put_unaligned_le32(params->head_len, cur_byte); 177562306a36Sopenharmony_ci cur_byte += 4; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (params->head_len > 0) 177862306a36Sopenharmony_ci memcpy(cur_byte, params->head, params->head_len); 177962306a36Sopenharmony_ci cur_byte += params->head_len; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci put_unaligned_le32(params->tail_len, cur_byte); 178262306a36Sopenharmony_ci cur_byte += 4; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (params->tail_len > 0) 178562306a36Sopenharmony_ci memcpy(cur_byte, params->tail, params->tail_len); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 178862306a36Sopenharmony_ci if (result) 178962306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send add beacon\n"); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci kfree(wid.val); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci return result; 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ciint wilc_del_beacon(struct wilc_vif *vif) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci int result; 179962306a36Sopenharmony_ci struct wid wid; 180062306a36Sopenharmony_ci u8 del_beacon = 0; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci wid.id = WID_DEL_BEACON; 180362306a36Sopenharmony_ci wid.type = WID_CHAR; 180462306a36Sopenharmony_ci wid.size = sizeof(char); 180562306a36Sopenharmony_ci wid.val = &del_beacon; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 180862306a36Sopenharmony_ci if (result) 180962306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send delete beacon\n"); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci return result; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ciint wilc_add_station(struct wilc_vif *vif, const u8 *mac, 181562306a36Sopenharmony_ci struct station_parameters *params) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci struct wid wid; 181862306a36Sopenharmony_ci int result; 181962306a36Sopenharmony_ci u8 *cur_byte; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci wid.id = WID_ADD_STA; 182262306a36Sopenharmony_ci wid.type = WID_BIN; 182362306a36Sopenharmony_ci wid.size = WILC_ADD_STA_LENGTH + 182462306a36Sopenharmony_ci params->link_sta_params.supported_rates_len; 182562306a36Sopenharmony_ci wid.val = kmalloc(wid.size, GFP_KERNEL); 182662306a36Sopenharmony_ci if (!wid.val) 182762306a36Sopenharmony_ci return -ENOMEM; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci cur_byte = wid.val; 183062306a36Sopenharmony_ci wilc_hif_pack_sta_param(cur_byte, mac, params); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 183362306a36Sopenharmony_ci if (result != 0) 183462306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send add station\n"); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci kfree(wid.val); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci return result; 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ciint wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct wid wid; 184462306a36Sopenharmony_ci int result; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci wid.id = WID_REMOVE_STA; 184762306a36Sopenharmony_ci wid.type = WID_BIN; 184862306a36Sopenharmony_ci wid.size = ETH_ALEN; 184962306a36Sopenharmony_ci wid.val = kzalloc(wid.size, GFP_KERNEL); 185062306a36Sopenharmony_ci if (!wid.val) 185162306a36Sopenharmony_ci return -ENOMEM; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (!mac_addr) 185462306a36Sopenharmony_ci eth_broadcast_addr(wid.val); 185562306a36Sopenharmony_ci else 185662306a36Sopenharmony_ci ether_addr_copy(wid.val, mac_addr); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 185962306a36Sopenharmony_ci if (result) 186062306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to del station\n"); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci kfree(wid.val); 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci return result; 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ciint wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN]) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct wid wid; 187062306a36Sopenharmony_ci int result; 187162306a36Sopenharmony_ci int i; 187262306a36Sopenharmony_ci u8 assoc_sta = 0; 187362306a36Sopenharmony_ci struct wilc_del_all_sta del_sta; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci memset(&del_sta, 0x0, sizeof(del_sta)); 187662306a36Sopenharmony_ci for (i = 0; i < WILC_MAX_NUM_STA; i++) { 187762306a36Sopenharmony_ci if (!is_zero_ether_addr(mac_addr[i])) { 187862306a36Sopenharmony_ci assoc_sta++; 187962306a36Sopenharmony_ci ether_addr_copy(del_sta.mac[i], mac_addr[i]); 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci if (!assoc_sta) 188462306a36Sopenharmony_ci return 0; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci del_sta.assoc_sta = assoc_sta; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci wid.id = WID_DEL_ALL_STA; 188962306a36Sopenharmony_ci wid.type = WID_STR; 189062306a36Sopenharmony_ci wid.size = (assoc_sta * ETH_ALEN) + 1; 189162306a36Sopenharmony_ci wid.val = (u8 *)&del_sta; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 189462306a36Sopenharmony_ci if (result) 189562306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send delete all station\n"); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return result; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ciint wilc_edit_station(struct wilc_vif *vif, const u8 *mac, 190162306a36Sopenharmony_ci struct station_parameters *params) 190262306a36Sopenharmony_ci{ 190362306a36Sopenharmony_ci struct wid wid; 190462306a36Sopenharmony_ci int result; 190562306a36Sopenharmony_ci u8 *cur_byte; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci wid.id = WID_EDIT_STA; 190862306a36Sopenharmony_ci wid.type = WID_BIN; 190962306a36Sopenharmony_ci wid.size = WILC_ADD_STA_LENGTH + 191062306a36Sopenharmony_ci params->link_sta_params.supported_rates_len; 191162306a36Sopenharmony_ci wid.val = kmalloc(wid.size, GFP_KERNEL); 191262306a36Sopenharmony_ci if (!wid.val) 191362306a36Sopenharmony_ci return -ENOMEM; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci cur_byte = wid.val; 191662306a36Sopenharmony_ci wilc_hif_pack_sta_param(cur_byte, mac, params); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 191962306a36Sopenharmony_ci if (result) 192062306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send edit station\n"); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci kfree(wid.val); 192362306a36Sopenharmony_ci return result; 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ciint wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout) 192762306a36Sopenharmony_ci{ 192862306a36Sopenharmony_ci struct wilc *wilc = vif->wilc; 192962306a36Sopenharmony_ci struct wid wid; 193062306a36Sopenharmony_ci int result; 193162306a36Sopenharmony_ci s8 power_mode; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (enabled) 193462306a36Sopenharmony_ci power_mode = WILC_FW_MIN_FAST_PS; 193562306a36Sopenharmony_ci else 193662306a36Sopenharmony_ci power_mode = WILC_FW_NO_POWERSAVE; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci wid.id = WID_POWER_MANAGEMENT; 193962306a36Sopenharmony_ci wid.val = &power_mode; 194062306a36Sopenharmony_ci wid.size = sizeof(char); 194162306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 194262306a36Sopenharmony_ci if (result) 194362306a36Sopenharmony_ci netdev_err(vif->ndev, "Failed to send power management\n"); 194462306a36Sopenharmony_ci else 194562306a36Sopenharmony_ci wilc->power_save_mode = enabled; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci return result; 194862306a36Sopenharmony_ci} 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ciint wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count, 195162306a36Sopenharmony_ci u8 *mc_list) 195262306a36Sopenharmony_ci{ 195362306a36Sopenharmony_ci int result; 195462306a36Sopenharmony_ci struct host_if_msg *msg; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci msg = wilc_alloc_work(vif, handle_set_mcast_filter, false); 195762306a36Sopenharmony_ci if (IS_ERR(msg)) 195862306a36Sopenharmony_ci return PTR_ERR(msg); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci msg->body.mc_info.enabled = enabled; 196162306a36Sopenharmony_ci msg->body.mc_info.cnt = count; 196262306a36Sopenharmony_ci msg->body.mc_info.mc_list = mc_list; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci result = wilc_enqueue_work(msg); 196562306a36Sopenharmony_ci if (result) { 196662306a36Sopenharmony_ci netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); 196762306a36Sopenharmony_ci kfree(msg); 196862306a36Sopenharmony_ci } 196962306a36Sopenharmony_ci return result; 197062306a36Sopenharmony_ci} 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ciint wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci struct wid wid; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci wid.id = WID_TX_POWER; 197762306a36Sopenharmony_ci wid.type = WID_CHAR; 197862306a36Sopenharmony_ci wid.val = &tx_power; 197962306a36Sopenharmony_ci wid.size = sizeof(char); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 198262306a36Sopenharmony_ci} 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ciint wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power) 198562306a36Sopenharmony_ci{ 198662306a36Sopenharmony_ci struct wid wid; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci wid.id = WID_TX_POWER; 198962306a36Sopenharmony_ci wid.type = WID_CHAR; 199062306a36Sopenharmony_ci wid.val = tx_power; 199162306a36Sopenharmony_ci wid.size = sizeof(char); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci return wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ciint wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci struct wid wid; 199962306a36Sopenharmony_ci int result; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci wid.id = WID_DEFAULT_MGMT_KEY_ID; 200262306a36Sopenharmony_ci wid.type = WID_CHAR; 200362306a36Sopenharmony_ci wid.size = sizeof(char); 200462306a36Sopenharmony_ci wid.val = &index; 200562306a36Sopenharmony_ci result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); 200662306a36Sopenharmony_ci if (result) 200762306a36Sopenharmony_ci netdev_err(vif->ndev, 200862306a36Sopenharmony_ci "Failed to send default mgmt key index\n"); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci return result; 201162306a36Sopenharmony_ci} 2012