| // SPDX-License-Identifier: GPL-2.0 |
| /* virt_wifi_simulation.c |
| * |
| * Regist ops to virt_wifi driver. |
| * |
| * And decide which simulation data need to simulate. |
| * |
| * Copyright (C) 2019 Google LLC |
| * |
| * Author: [email protected] |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/sched.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/slab.h> |
| #include <net/cfg80211.h> |
| #include <net/virt_wifi.h> |
| #include "virt_wifi_simulation.h" |
| #include "virt_wifi_data.h" |
| |
| static struct virt_wifi_network_simulation ops = { |
| .notify_device_open = notify_device_open, |
| .notify_device_stop = notify_device_stop, |
| .notify_scan_trigger = notify_scan_trigger, |
| .generate_virt_scan_result = generate_virt_scan_result, |
| }; |
| |
| struct device wlan_simulation_device; |
| |
| struct information_element { |
| u8 tag; |
| u8 len; |
| u8 *data; |
| } __packed; |
| |
| char scan_result_switch_factor; |
| int total_configured_ap_num; |
| int total_configured_scan_result; |
| int current_scan_trigger_count; |
| u64 current_scan_trigger_time; |
| u64 wifi_enable_tsf; |
| int last_scan_config_index; |
| struct access_point **ap_list; |
| struct scan_config **scan_list; |
| |
| static u8 *convert_bssid(char *bssid_str) |
| { |
| char *token, *tmp; |
| unsigned long val; |
| int parser_index = 0; |
| u8 *converted_bssid = kzalloc(ETH_ALEN, GFP_KERNEL); |
| |
| if (!converted_bssid) |
| return NULL; |
| |
| tmp = kstrdup(bssid_str, GFP_KERNEL); |
| if (!tmp) { |
| kfree(converted_bssid); |
| return NULL; |
| } |
| |
| do { |
| token = strsep(&tmp, ":"); |
| if (token != NULL) { |
| kstrtoul(token, 16, &val); |
| converted_bssid[parser_index] = val; |
| parser_index++; |
| if (parser_index == ETH_ALEN) |
| break; |
| } |
| } while (token != NULL); |
| return converted_bssid; |
| } |
| |
| static u8 *generate_ie(struct access_point *ap, int *ie_len_ptr) |
| { |
| int ssid_len; |
| u8 *ie = NULL; |
| char rsn_data_aes_psk[20] = {0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, |
| 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, |
| 0x01, 0x00, 0x00, 0x0f, 0xac, 0x02, |
| 0x0c, 0x00}; |
| struct information_element *ssid = |
| kzalloc(sizeof(struct information_element), GFP_KERNEL); |
| struct information_element *rsn = NULL; |
| |
| if (!ssid) |
| goto error; |
| ssid_len = strlen(ap->ssid); |
| ssid->tag = WLAN_EID_SSID; |
| ssid->len = ssid_len; |
| |
| if (!strcmp(ap->security_type, TYPE_WPA2)) { |
| pr_debug("ap : %s is WPA2", ap->ssid); |
| rsn = kzalloc(sizeof(struct information_element), GFP_KERNEL); |
| if (!rsn) |
| goto error; |
| rsn->tag = WLAN_EID_RSN; |
| rsn->len = 20; |
| } |
| *ie_len_ptr = ssid->len + 2; |
| if (rsn) |
| *ie_len_ptr += (rsn->len + 2); |
| |
| ie = kzalloc(*ie_len_ptr, GFP_KERNEL); |
| if (ie) { |
| memcpy(ie, ssid, 2); |
| memcpy(ie+2, ap->ssid, ssid_len); |
| } |
| if (rsn && ie) { |
| memcpy(ie + ssid_len + 2, rsn, 2); |
| memcpy(ie + ssid_len + 4, rsn_data_aes_psk, rsn->len); |
| } |
| error: |
| kfree(rsn); |
| kfree(ssid); |
| return ie; |
| } |
| |
| static int select_scan_config(void) |
| { |
| int index; |
| u64 delta_time = div_u64(current_scan_trigger_time - wifi_enable_tsf, |
| 1000000000); |
| |
| for (index = last_scan_config_index; |
| index < total_configured_scan_result; index++) { |
| if (scan_result_switch_factor == 'C' && |
| (scan_list[index]->control_setting > |
| current_scan_trigger_count)) { |
| break; |
| } |
| if (scan_result_switch_factor == 'T' && |
| scan_list[index]->control_setting > delta_time) { |
| break; |
| } |
| } |
| if (index == total_configured_scan_result) |
| index = total_configured_scan_result - 1; |
| if (last_scan_config_index != index) { |
| last_scan_config_index = index; |
| pr_info("%s switch config to %d", __func__, index); |
| } |
| return index; |
| } |
| |
| int get_virt_scan_result(struct wiphy *wiphy) |
| { |
| int ie_len, ret = 0; |
| int targer_scan_config = select_scan_config(); |
| struct list_head *pos; |
| struct cfg80211_bss *informed_bss; |
| struct ieee80211_channel *channel; |
| u8 *bssid, *ie; |
| |
| list_for_each(pos, scan_list[targer_scan_config]->scanList) { |
| struct scan_ap_info *ap_info = |
| list_entry(pos, struct scan_ap_info, list); |
| ie_len = 0; |
| if (ap_info->ap_index > total_configured_ap_num) { |
| pr_err("The ap_index %d doesn't exist in cf_ap_list", |
| ap_info->ap_index); |
| ret = -1; |
| break; |
| } |
| channel = |
| ieee80211_get_channel(wiphy, |
| ap_list[ap_info->ap_index]->channel); |
| bssid = convert_bssid(ap_list[ap_info->ap_index]->bssid); |
| if (!bssid) { |
| ret = -1; |
| goto error; |
| } |
| ie = generate_ie(ap_list[ap_info->ap_index], &ie_len); |
| if (!ie) { |
| kfree(bssid); |
| ret = -1; |
| goto error; |
| } |
| informed_bss = cfg80211_inform_bss(wiphy, channel, |
| CFG80211_BSS_FTYPE_PRESP, |
| bssid, |
| ktime_get_ns(), |
| WLAN_CAPABILITY_ESS, 0, |
| (void *)ie, ie_len, |
| DBM_TO_MBM(ap_info->signal), |
| GFP_KERNEL); |
| cfg80211_put_bss(wiphy, informed_bss); |
| kfree(ie); |
| kfree(bssid); |
| ie = NULL; |
| bssid = NULL; |
| } |
| error: |
| return ret; |
| } |
| |
| int do_virt_scan(void) |
| { |
| current_scan_trigger_count++; |
| current_scan_trigger_time = ktime_get_ns(); |
| pr_info("%s enter, count = %d, trigger time is |%lu|", |
| __func__, current_scan_trigger_count, |
| current_scan_trigger_time); |
| return 0; |
| } |
| |
| int virt_wifi_simulation_clean_up(void) |
| { |
| pr_info("%s enter", __func__); |
| total_configured_ap_num = 0; |
| total_configured_scan_result = 0; |
| current_scan_trigger_count = 0; |
| current_scan_trigger_time = 0; |
| wifi_enable_tsf = 0; |
| last_scan_config_index = 0; |
| data_clean_up(); |
| return 0; |
| } |
| |
| void notify_device_open(struct net_device *dev) |
| { |
| if (!load_simulation_data(&wlan_simulation_device)) { |
| virt_wifi_simulation_clean_up(); |
| return; |
| } |
| ap_list = get_ap_list(&total_configured_ap_num); |
| scan_list = get_scan_config_list(&total_configured_scan_result, |
| &scan_result_switch_factor); |
| wifi_enable_tsf = ktime_get_ns(); |
| pr_info("%s - total ap num=%d and total scan config num = %d,", |
| __func__, total_configured_ap_num, |
| total_configured_scan_result); |
| pr_info(" %s - switch factor = %c and enable time :|%lu|", |
| __func__, scan_result_switch_factor, wifi_enable_tsf); |
| } |
| |
| void notify_device_stop(struct net_device *dev) |
| { |
| virt_wifi_unregister_network_simulation(); |
| virt_wifi_simulation_clean_up(); |
| virt_wifi_register_network_simulation(&ops); |
| } |
| |
| void notify_scan_trigger(struct wiphy *wiphy, |
| struct cfg80211_scan_request *request) |
| { |
| do_virt_scan(); |
| } |
| |
| int generate_virt_scan_result(struct wiphy *wiphy) |
| { |
| if ((total_configured_ap_num && total_configured_scan_result)) |
| get_virt_scan_result(wiphy); |
| return 0; |
| } |
| |
| int __init init_virt_data_simulation_module(void) |
| { |
| pr_info("%s - enter", __func__); |
| virt_wifi_register_network_simulation(&ops); |
| device_register(&wlan_simulation_device); |
| return 0; |
| } |
| |
| void __exit exit_virt_data_simulation_module(void) |
| { |
| pr_info("%s - enter", __func__); |
| device_unregister(&wlan_simulation_device); |
| virt_wifi_simulation_clean_up(); |
| virt_wifi_unregister_network_simulation(); |
| } |
| |
| module_init(init_virt_data_simulation_module); |
| module_exit(exit_virt_data_simulation_module); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Les Lee <[email protected]>"); |
| MODULE_DESCRIPTION("Module for the wifi virt driver data simulation."); |
| |