/* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: Implements CM UTF */ #ifdef FEATURE_CM_UTF_ENABLE #include #include #include #include #include #define CM_UTF_LIST_SIZE 1 qdf_list_t wlan_cm_utf_list; static const char *cm_utf_test_names[] = { "CONNECT_SUCCESS", "DISCONNECT_SUCCESS", "PEER_CREATE_FAILURE", "PEER_CREATE_TIMEOUT", "PEER_DELETE_TIMEOUT", "AUTH_FAILURE", "AUTH_TIMEOUT", "ASSOC_FAILURE", "ASSOC_TIMEOUT", "CONNECT_SCAN_FAILURE", "CONNECT_SER_TIMEOUT", "DISCONNECT_SER_TIMEOUT", "CONNECT_SER_FAILED", "DISCONNECT_SER_FAIL", }; /* Structure to maintain debug information */ struct cm_utf_debugfs_info { const char *name; const struct file_operations *ops; }; #define DEBUG_FOO(func_base) { .name = #func_base, \ .ops = &wlan_cm_utf_##func_base##_ops } /* * wlan_cm_utf_##func_base##_open() - Open debugfs entry for respective command * and event buffer. * @inode: node for debug dir entry * @file: file handler * * Return: open status */ #define GENERATE_DEBUG_STRUCTS(func_base) \ static int wlan_cm_utf_##func_base##_open(struct inode *inode, \ struct file *file) \ { \ return single_open(file, wlan_cm_utf_##func_base##_show, \ inode->i_private); \ } \ \ static const struct file_operations wlan_cm_utf_##func_base##_ops = { \ .open = wlan_cm_utf_##func_base##_open, \ .read = seq_read, \ .llseek = seq_lseek, \ .write = wlan_cm_utf_##func_base##_write, \ .release = single_release, \ }; GENERATE_DEBUG_STRUCTS(scan_db_update); GENERATE_DEBUG_STRUCTS(cm_test_id); struct cm_utf_debugfs_info cm_utf_debugfs_infos[NUM_UTF_DEBUGFS_INFOS] = { DEBUG_FOO(scan_db_update), DEBUG_FOO(cm_test_id), }; /** * wlan_cm_utf_debugfs_create() - Create debugfs entry for cm db * @cm_utf: CM UTF object * * Return: QDF_STATUS */ static QDF_STATUS wlan_cm_utf_debugfs_create(struct wlan_cm_utf *cm_utf) { struct wlan_objmgr_vdev *vdev = cm_utf->vdev; struct wlan_objmgr_pdev *pdev; uint8_t pdev_id; uint8_t vdev_id; char name[32]; if (cm_utf->debugfs_de[0] || cm_utf->debugfs_de[1]) { mlme_info("CM UTF debugfs file already exist"); return QDF_STATUS_SUCCESS; } pdev = wlan_vdev_get_pdev(vdev); if (!pdev) { mlme_err("Pdev is Null"); return QDF_STATUS_E_FAILURE; } pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); vdev_id = wlan_vdev_get_id(vdev); snprintf(name, sizeof(name), "CM_UTF_PDEV%u_VDEV%u_SCAN", pdev_id, vdev_id); cm_utf->debugfs_de[0] = debugfs_create_file( name, 0644, qdf_debugfs_get_root(), cm_utf, cm_utf_debugfs_infos[0].ops); if (!cm_utf->debugfs_de[0]) { mlme_err("Failed to create debugfs entry"); return QDF_STATUS_E_FAILURE; } snprintf(name, sizeof(name), "CM_UTF_PDEV%u_VDEV%u_UTF", pdev_id, vdev_id); cm_utf->debugfs_de[1] = debugfs_create_file( name, 0644, qdf_debugfs_get_root(), cm_utf, cm_utf_debugfs_infos[1].ops); if (!cm_utf->debugfs_de[1]) { mlme_err("Failed to create debugfs entry"); debugfs_remove(cm_utf->debugfs_de[0]); cm_utf->debugfs_de[0] = NULL; return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wlan_cm_utf_debugfs_remove: Remove connection manager UTF debugfs entries * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_debugfs_remove(struct wlan_cm_utf *cm_utf) { if (cm_utf->debugfs_de[0]) { qdf_debugfs_remove_file(cm_utf->debugfs_de[0]); cm_utf->debugfs_de[0] = NULL; } if (cm_utf->debugfs_de[1]) { qdf_debugfs_remove_file(cm_utf->debugfs_de[1]); cm_utf->debugfs_de[1] = NULL; } } /** * wlan_cm_utf_bss_peer_create_rsp: Connection manager UTF bss peer * create response * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_bss_peer_create_rsp(struct wlan_cm_utf *cm_utf) { struct qdf_mac_addr *peer_mac; QDF_STATUS status; peer_mac = cm_utf->utf_node.peer_mac; switch (cm_utf->test_id) { case CM_UTF_ID_PEER_CREATE_FAILURE: status = QDF_STATUS_E_FAILURE; break; default: status = QDF_STATUS_SUCCESS; break; } wlan_cm_bss_peer_create_rsp(cm_utf->vdev, status, peer_mac); } /** * wlan_cm_utf_bss_peer_delete_rsp: Connection manager UTF bss peer * delete response * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_bss_peer_delete_rsp(struct wlan_cm_utf *cm_utf) { wlan_cm_bss_peer_delete_rsp(cm_utf->vdev, QDF_STATUS_SUCCESS); } /** * wlan_cm_utf_connect_rsp: Connection manager UTF connect response * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_connect_rsp(struct wlan_cm_utf *cm_utf) { struct wlan_cm_connect_resp *cm_conn_rsp; struct wlan_cm_vdev_connect_req conn_req = cm_utf->utf_node.conn_req; cm_conn_rsp = qdf_mem_malloc(sizeof(struct wlan_cm_connect_resp)); if (!cm_conn_rsp) { mlme_err("failed ta allocate memory"); return; } qdf_mem_zero(cm_conn_rsp, sizeof(struct wlan_cm_connect_resp)); cm_conn_rsp->vdev_id = conn_req.vdev_id; cm_conn_rsp->cm_id = conn_req.cm_id; cm_conn_rsp->aid = 1; cm_conn_rsp->connect_status = QDF_STATUS_E_FAILURE; cm_conn_rsp->status_code = 0; cm_conn_rsp->freq = conn_req.bss->entry->channel.chan_freq; cm_conn_rsp->connect_ies.bcn_probe_rsp.ptr = conn_req.bss->entry->raw_frame.ptr; cm_conn_rsp->connect_ies.bcn_probe_rsp.len = conn_req.bss->entry->raw_frame.len; cm_conn_rsp->bssid = conn_req.bss->entry->bssid; cm_conn_rsp->ssid = conn_req.bss->entry->ssid; switch (cm_utf->test_id) { case CM_UTF_ID_AUTH_FAILURE: cm_conn_rsp->reason = CM_AUTH_FAILED; break; case CM_UTF_ID_AUTH_TIMEOUT: cm_conn_rsp->reason = CM_AUTH_TIMEOUT; break; case CM_UTF_ID_ASSOC_FAILURE: cm_conn_rsp->reason = CM_ASSOC_FAILED; break; case CM_UTF_ID_ASSOC_TIMEOUT: cm_conn_rsp->reason = CM_ASSOC_TIMEOUT; break; default: cm_conn_rsp->connect_status = QDF_STATUS_SUCCESS; break; } wlan_cm_connect_rsp(cm_utf->vdev, cm_conn_rsp); qdf_mem_free(cm_conn_rsp); } /** * wlan_cm_utf_disconnect_rsp: Connection manager UTF disconnect response * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_disconnect_rsp(struct wlan_cm_utf *cm_utf) { struct wlan_cm_discon_rsp *cm_discon_rsp; cm_discon_rsp = qdf_mem_malloc(sizeof(struct wlan_cm_discon_rsp)); if (!cm_discon_rsp) { mlme_err("failed ta allocate memory"); return; } cm_discon_rsp->req = cm_utf->utf_node.disconn_req; wlan_cm_disconnect_rsp(cm_utf->vdev, cm_discon_rsp); qdf_mem_free(cm_discon_rsp); } /** * wlan_cm_utf_peer_del_ind: Connection manager UTF peer delete indication * @cm_utf: Connection Manager UTF object * * Return: None */ static void wlan_cm_utf_peer_del_ind(struct wlan_cm_utf *cm_utf) { struct qdf_mac_addr peer_mac; peer_mac.bytes[0] = 0x1; peer_mac.bytes[1] = 0x2; peer_mac.bytes[2] = 0x3; peer_mac.bytes[3] = 0x4; peer_mac.bytes[4] = 0x5; peer_mac.bytes[5] = 0x6; wlan_cm_bss_peer_delete_ind(cm_utf->vdev, &peer_mac); } static void wlan_cm_utf_stop_test(void *arg) { struct wlan_cm_utf *cm_utf = (struct wlan_cm_utf *)arg; mlme_err("No Response from CM"); cm_utf->test_id = CM_UTF_ID_MAX; } static void wlan_cm_utf_deliver_event(void *arg) { struct wlan_cm_utf *cm_utf = (struct wlan_cm_utf *)arg; qdf_sched_work(NULL, &cm_utf->cm_utf_work); } /** * wlan_cm_utf_default_connect_param: Update default connect req params * for connection manager UTF * @cm_utf: Connection Manager UTF object * * Return: None */ static QDF_STATUS wlan_cm_utf_default_connect_param(struct wlan_cm_utf *cm_utf) { cm_utf->req.vdev_id = wlan_vdev_get_id(cm_utf->vdev); cm_utf->req.source = CM_OSIF_CONNECT; cm_utf->req.bssid.bytes[0] = 0x1; cm_utf->req.bssid.bytes[1] = 0x2; cm_utf->req.bssid.bytes[2] = 0x3; cm_utf->req.bssid.bytes[3] = 0x4; cm_utf->req.bssid.bytes[4] = 0x5; cm_utf->req.bssid.bytes[5] = 0x6; cm_utf->req.ssid.length = 6; qdf_str_lcopy(cm_utf->req.ssid.ssid, "CM_STA", cm_utf->req.ssid.length); cm_utf->req.chan_freq = 5200; cm_utf->req.crypto.wep_keys.key_len = 0; return QDF_STATUS_SUCCESS; } static void wlan_cm_utf_work_cb(void *arg) { struct wlan_cm_utf *cm_utf = (struct wlan_cm_utf *)arg; enum wlan_cm_utf_evt event_id = cm_utf->utf_node.evt_id; switch (event_id) { case CM_UTF_BSS_PEER_CREATE_RESP: wlan_cm_utf_bss_peer_create_rsp(cm_utf); break; case CM_UTF_BSS_PEER_DELETE_RESP: wlan_cm_utf_bss_peer_delete_rsp(cm_utf); break; case CM_UTF_CONNECT_RESP: wlan_cm_utf_connect_rsp(cm_utf); break; case CM_UTF_DISCONNECT_RESP: wlan_cm_utf_disconnect_rsp(cm_utf); break; case CM_UTF_PEER_DELETE_IND: wlan_cm_utf_peer_del_ind(cm_utf); break; default: break; } } QDF_STATUS wlan_cm_utf_attach(struct wlan_objmgr_vdev *vdev) { struct wlan_cm_utf *cm_utf; QDF_STATUS status; if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE) { mlme_err("Vdev is not a STA vdev"); return QDF_STATUS_E_FAILURE; } if (qdf_list_size(&wlan_cm_utf_list) == CM_UTF_LIST_SIZE) { mlme_err("List is at max size"); return QDF_STATUS_E_FAILURE; } if (!qdf_list_size(&wlan_cm_utf_list)) qdf_list_create(&wlan_cm_utf_list, CM_UTF_LIST_SIZE); cm_utf = (struct wlan_cm_utf *) qdf_mem_malloc(sizeof(struct wlan_cm_utf)); if (!cm_utf) { mlme_err("Failed to allocate CM utf context"); qdf_list_destroy(&wlan_cm_utf_list); return QDF_STATUS_E_NOMEM; } cm_utf->vdev = vdev; cm_utf->test_id = CM_UTF_ID_MAX; wlan_cm_utf_default_connect_param(cm_utf); status = wlan_cm_utf_debugfs_create(cm_utf); if (QDF_IS_STATUS_ERROR(status)) { qdf_mem_free(cm_utf); qdf_list_destroy(&wlan_cm_utf_list); return status; } qdf_timer_init(NULL, &cm_utf->cm_utf_timer, wlan_cm_utf_deliver_event, (void *)cm_utf, QDF_TIMER_TYPE_WAKE_APPS); qdf_timer_init(NULL, &cm_utf->cm_utf_test_timer, wlan_cm_utf_stop_test, (void *)cm_utf, QDF_TIMER_TYPE_WAKE_APPS); qdf_create_work(NULL, &cm_utf->cm_utf_work, wlan_cm_utf_work_cb, cm_utf); qdf_list_insert_back(&wlan_cm_utf_list, &cm_utf->cm_utf_node); mlme_err("CM UTF attach Success"); return QDF_STATUS_SUCCESS; } static struct wlan_cm_utf *wlan_cm_get_utf(struct wlan_objmgr_vdev *vdev) { struct wlan_cm_utf *cm_utf; qdf_list_node_t *node; qdf_list_node_t *next_node; if (!qdf_list_size(&wlan_cm_utf_list)) { mlme_err("UTF list is empty"); return NULL; } if (qdf_list_peek_front(&wlan_cm_utf_list, &next_node) != QDF_STATUS_SUCCESS) { mlme_err("UTF list is empty"); return NULL; } do { node = next_node; cm_utf = qdf_container_of(node, struct wlan_cm_utf, cm_utf_node); if (cm_utf->vdev == vdev) return cm_utf; } while (qdf_list_peek_next(&wlan_cm_utf_list, node, &next_node) == QDF_STATUS_SUCCESS); return NULL; } void wlan_cm_utf_detach(struct wlan_objmgr_vdev *vdev) { struct wlan_cm_utf *cm_utf; QDF_STATUS status; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("UTF not initialized for the vdev %d", wlan_vdev_get_id(vdev)); return; } status = qdf_list_remove_node(&wlan_cm_utf_list, &cm_utf->cm_utf_node); if (QDF_IS_STATUS_SUCCESS(status)) { qdf_timer_free(&cm_utf->cm_utf_timer); qdf_timer_free(&cm_utf->cm_utf_test_timer); if (cm_utf->req.assoc_ie.ptr) { qdf_mem_free(cm_utf->req.assoc_ie.ptr); cm_utf->req.assoc_ie.ptr = NULL; } if (cm_utf->req.crypto.wep_keys.key) { qdf_mem_free(cm_utf->req.crypto.wep_keys.key); cm_utf->req.crypto.wep_keys.key = NULL; } qdf_disable_work(&cm_utf->cm_utf_work); wlan_cm_utf_debugfs_remove(cm_utf); qdf_mem_free(cm_utf); mlme_err("CM UTF Deinit Success"); return; } mlme_err("Failed to remove UTF node"); } QDF_STATUS wlan_cm_utf_bss_peer_create_req(struct wlan_objmgr_vdev *vdev, struct qdf_mac_addr *peer_mac) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } switch (cm_utf->test_id) { case CM_UTF_ID_PEER_CREATE_TIMEOUT: break; default: cm_utf->utf_node.peer_mac = peer_mac; cm_utf->utf_node.evt_id = CM_UTF_BSS_PEER_CREATE_RESP; qdf_timer_start(&cm_utf->cm_utf_timer, 100); break; } return QDF_STATUS_SUCCESS; } QDF_STATUS wlan_cm_utf_connect_req_active( struct wlan_objmgr_vdev *vdev, struct wlan_cm_vdev_connect_req *vdev_connect_req) { //Resp API to be added return QDF_STATUS_SUCCESS; } QDF_STATUS wlan_cm_utf_connect_req( struct wlan_objmgr_vdev *vdev, struct wlan_cm_vdev_connect_req *vdev_connect_req) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } switch (cm_utf->test_id) { case CM_UTF_ID_CONNECT_SER_TIMEOUT: break; default: qdf_mem_copy(&cm_utf->utf_node.conn_req, vdev_connect_req, sizeof(struct wlan_cm_vdev_connect_req)); cm_utf->utf_node.evt_id = CM_UTF_CONNECT_RESP; qdf_timer_start(&cm_utf->cm_utf_timer, 100); break; } return QDF_STATUS_SUCCESS; } QDF_STATUS wlan_cm_utf_disconnect_req( struct wlan_objmgr_vdev *vdev, struct wlan_cm_vdev_discon_req *vdev_disconnect_req) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } switch (cm_utf->test_id) { case CM_UTF_ID_DISCONNECT_SER_TIMEOUT: break; default: qdf_mem_copy(&cm_utf->utf_node.disconn_req, vdev_disconnect_req, sizeof(struct wlan_cm_vdev_discon_req)); cm_utf->utf_node.evt_id = CM_UTF_PEER_DELETE_IND; qdf_timer_start(&cm_utf->cm_utf_timer, 100); break; } return QDF_STATUS_SUCCESS; } QDF_STATUS wlan_cm_utf_bss_peer_delete_req(struct wlan_objmgr_vdev *vdev) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } switch (cm_utf->test_id) { case CM_UTF_ID_PEER_DELETE_TIMEOUT: break; default: cm_utf->utf_node.evt_id = CM_UTF_BSS_PEER_DELETE_RESP; qdf_timer_start(&cm_utf->cm_utf_timer, 100); break; } return QDF_STATUS_SUCCESS; } QDF_STATUS wlan_cm_utf_vdev_down(struct wlan_objmgr_vdev *vdev) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } switch (cm_utf->test_id) { default: cm_utf->utf_node.evt_id = CM_UTF_DISCONNECT_RESP; qdf_timer_start(&cm_utf->cm_utf_timer, 100); break; } return QDF_STATUS_SUCCESS; } static QDF_STATUS wlan_cm_utf_validate_test(struct wlan_cm_utf *cm_utf, struct wlan_cm_connect_resp *cm_conn_rsp) { QDF_STATUS status = QDF_STATUS_E_FAILURE; switch (cm_utf->test_id) { case CM_UTF_ID_PEER_CREATE_FAILURE: case CM_UTF_ID_PEER_CREATE_TIMEOUT: if (cm_conn_rsp->reason == CM_PEER_CREATE_FAILED) { // check (CM state to be valid as per test in all cases) status = QDF_STATUS_SUCCESS; } break; case CM_UTF_ID_CONNECT_SER_TIMEOUT: case CM_UTF_ID_CONNECT_SER_FAILED: if (cm_conn_rsp->reason == CM_SER_FAILURE) status = QDF_STATUS_SUCCESS; break; case CM_UTF_ID_CONNECT_SCAN_FAILURE: if (cm_conn_rsp->reason == CM_NO_CANDIDATE_FOUND) status = QDF_STATUS_SUCCESS; break; case CM_UTF_ID_AUTH_FAILURE: if (cm_conn_rsp->reason == CM_AUTH_FAILED) status = QDF_STATUS_SUCCESS; break; case CM_UTF_ID_AUTH_TIMEOUT: if (cm_conn_rsp->reason == CM_AUTH_TIMEOUT) status = QDF_STATUS_SUCCESS; break; case CM_UTF_ID_ASSOC_FAILURE: if (cm_conn_rsp->reason == CM_ASSOC_FAILED) status = QDF_STATUS_SUCCESS; break; case CM_UTF_ID_ASSOC_TIMEOUT: if (cm_conn_rsp->reason == CM_ASSOC_TIMEOUT) status = QDF_STATUS_SUCCESS; break; default: if (cm_conn_rsp->connect_status == QDF_STATUS_SUCCESS) status = QDF_STATUS_SUCCESS; break; } return status; } /** * wlan_cm_utf_osif_connect_cb: Connection manager UTF * connect callback handler * @vdev: VDEV object * @cm_conn_rsp: Connect Response * * Return: None */ static QDF_STATUS wlan_cm_utf_osif_connect_cb(struct wlan_objmgr_vdev *vdev, struct wlan_cm_connect_resp *cm_conn_rsp) { struct wlan_cm_utf *cm_utf; QDF_STATUS status = QDF_STATUS_E_FAILURE; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } if (cm_conn_rsp->cm_id != cm_utf->last_cmd_id || cm_utf->last_cmd_source != CM_OSIF_CONNECT) { mlme_err("Incorrect cmd id/source"); return QDF_STATUS_E_FAILURE; } status = wlan_cm_utf_validate_test(cm_utf, cm_conn_rsp); if (status == QDF_STATUS_SUCCESS) mlme_err("Test case Success for Test: %s", cm_utf_test_names[cm_utf->test_id]); else mlme_err("Test case Failed for Test: %s", cm_utf_test_names[cm_utf->test_id]); qdf_timer_stop(&cm_utf->cm_utf_test_timer); cm_utf->test_id = CM_UTF_ID_MAX; return status; } /** * wlan_cm_utf_osif_disconnect_cb: Connection manager UTF * disconnect callback handler * @vdev: VDEV object * @cm_disconn_rsp: Disconnect Response * * Return: None */ static QDF_STATUS wlan_cm_utf_osif_disconnect_cb( struct wlan_objmgr_vdev *vdev, struct wlan_cm_discon_rsp *cm_disconn_rsp) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } if (cm_disconn_rsp->req.cm_id != cm_utf->last_cmd_id || cm_disconn_rsp->req.req.source != cm_utf->last_cmd_source) { mlme_err("Incorrect cmd id/source"); return QDF_STATUS_E_FAILURE; } mlme_err("Test case Success for Test: %s", cm_utf_test_names[cm_utf->test_id]); qdf_timer_stop(&cm_utf->cm_utf_test_timer); cm_utf->test_id = CM_UTF_ID_MAX; return QDF_STATUS_SUCCESS; } /** * wlan_cm_utf_update_conn_id_and_src_cb: Connection manager UTF * callback to update connect id and source of the connect request * @vdev: VDEV object * @source: connect source * @cm_id: connect id * * Return: None */ static QDF_STATUS wlan_cm_utf_update_conn_id_and_src_cb( struct wlan_objmgr_vdev *vdev, enum wlan_cm_source source, wlan_cm_id cm_id) { struct wlan_cm_utf *cm_utf; cm_utf = wlan_cm_get_utf(vdev); if (!cm_utf) { mlme_err("cm_utf is Null"); return QDF_STATUS_E_FAILURE; } cm_utf->last_cmd_id = cm_id; cm_utf->last_cmd_source = source; return QDF_STATUS_SUCCESS; } static struct mlme_cm_ops cm_ops = { .mlme_cm_connect_complete_cb = wlan_cm_utf_osif_connect_cb, .mlme_cm_failed_candidate_cb = NULL, .mlme_cm_update_id_and_src_cb = wlan_cm_utf_update_conn_id_and_src_cb, .mlme_cm_disconnect_complete_cb = wlan_cm_utf_osif_disconnect_cb, .mlme_cm_disconnect_start_cb = NULL, }; static struct mlme_cm_ops *osif_cm_get_global_ops(void) { return &cm_ops; } QDF_STATUS osif_cm_utf_register_cb(void) { mlme_set_osif_cm_cb(osif_cm_get_global_ops); return QDF_STATUS_SUCCESS; } /** * wlan_cm_utf_update_connect_param: API to update connect req params * @cm_utf: CM UTF object * @buffer: user input buffer * * Return: None * */ static void wlan_cm_utf_update_connect_param(struct wlan_cm_utf *cm_utf, char *buffer) { char *token; uint8_t idx = 0; token = qdf_str_sep(&buffer, "\n"); if (!token) { mlme_err("Invalid Parameters"); return; } if (cm_utf->req.assoc_ie.ptr) { qdf_mem_free(cm_utf->req.assoc_ie.ptr); cm_utf->req.assoc_ie.ptr = NULL; } if (cm_utf->req.crypto.wep_keys.key) { qdf_mem_free(cm_utf->req.crypto.wep_keys.key); cm_utf->req.crypto.wep_keys.key = NULL; } qdf_mem_zero(cm_utf->req.ssid.ssid, WLAN_SSID_MAX_LEN); if (sscanf(token, "%2x:%2x:%2x:%2x:%2x:%2x ,%2x:%2x:%2x:%2x:%2x:%2x " ",%2x:%2x:%2x:%2x:%2x:%2x ,%u ,%u ,%u ,%u ,%u ,%u ,%u ,%u " ",%u ,%u ,%u ,%u ,%u ,%s", (unsigned int *)&cm_utf->req.bssid.bytes[0], (unsigned int *)&cm_utf->req.bssid.bytes[1], (unsigned int *)&cm_utf->req.bssid.bytes[2], (unsigned int *)&cm_utf->req.bssid.bytes[3], (unsigned int *)&cm_utf->req.bssid.bytes[4], (unsigned int *)&cm_utf->req.bssid.bytes[5], (unsigned int *)&cm_utf->req.prev_bssid.bytes[0], (unsigned int *)&cm_utf->req.prev_bssid.bytes[1], (unsigned int *)&cm_utf->req.prev_bssid.bytes[2], (unsigned int *)&cm_utf->req.prev_bssid.bytes[3], (unsigned int *)&cm_utf->req.prev_bssid.bytes[4], (unsigned int *)&cm_utf->req.prev_bssid.bytes[5], (unsigned int *)&cm_utf->req.bssid_hint.bytes[0], (unsigned int *)&cm_utf->req.bssid_hint.bytes[1], (unsigned int *)&cm_utf->req.bssid_hint.bytes[2], (unsigned int *)&cm_utf->req.bssid_hint.bytes[3], (unsigned int *)&cm_utf->req.bssid_hint.bytes[4], (unsigned int *)&cm_utf->req.bssid_hint.bytes[5], &cm_utf->req.chan_freq, &cm_utf->req.crypto.wpa_versions, &cm_utf->req.crypto.auth_type, &cm_utf->req.crypto.group_cipher, &cm_utf->req.crypto.ciphers_pairwise, &cm_utf->req.crypto.akm_suites, (unsigned int *)&cm_utf->req.crypto.rsn_caps, &cm_utf->req.crypto.mgmt_ciphers, (unsigned int *)&cm_utf->req.ht_caps, (unsigned int *)&cm_utf->req.ht_caps_mask, &cm_utf->req.vht_caps, &cm_utf->req.vht_caps_mask, (unsigned int *)&cm_utf->req.ssid.length, cm_utf->req.ssid.ssid) != 32) { mlme_err("Invalid connect req params"); return; } qdf_err("Updated connect params %s", buffer); while ((token = qdf_str_sep(&buffer, ",")) != NULL) { switch (idx) { case 0: if (sscanf(token, "%u", &cm_utf->req.assoc_ie.len) != 1) return; if (!cm_utf->req.assoc_ie.len) { idx += 2; break; } idx++; break; case 1: cm_utf->req.assoc_ie.ptr = (uint8_t *)qdf_mem_malloc(cm_utf->req.assoc_ie.len); if (!cm_utf->req.assoc_ie.ptr) { mlme_err("Failed to alloc memory"); return; } qdf_mem_copy(cm_utf->req.assoc_ie.ptr, token, cm_utf->req.assoc_ie.len); idx++; break; case 2: if (sscanf(token, "%u", (unsigned int *)&cm_utf->req.crypto.wep_keys.key_len) != 1) return; if (!cm_utf->req.crypto.wep_keys.key_len) return; idx++; break; case 3: cm_utf->req.crypto.wep_keys.key = (uint8_t *)qdf_mem_malloc(cm_utf->req.crypto.wep_keys.key_len); if (!cm_utf->req.crypto.wep_keys.key) { mlme_err("Failed to alloc memory"); return; } qdf_mem_copy(cm_utf->req.crypto.wep_keys.key, token, cm_utf->req.crypto.wep_keys.key_len); break; default: break; } } } /** * wlan_cm_utf_start_test: Connection manager UTF entry function * @cm_utf: Connection Manager UTF object * @buffer: test command buffer * */ static void wlan_cm_utf_start_test(struct wlan_cm_utf *cm_utf, void *buffer) { int reason = 0, ret; uint8_t i = 0; char *token; char *token2; char *buff = (char *)buffer; struct wlan_cm_disconnect_req req; if (cm_utf->test_id != CM_UTF_ID_MAX) { mlme_err("Test Already in progress"); return; } token = qdf_str_sep(&buff, " "); if (token) { if (!qdf_str_cmp(token, "CONNECT_REQ")) { mlme_err("Update Connect Params"); wlan_cm_utf_update_connect_param(cm_utf, buff); return; } token2 = qdf_str_sep(&token, "\n"); for (i = 0; i < CM_UTF_ID_MAX; i++) { if (!qdf_str_cmp(token2, cm_utf_test_names[i])) { cm_utf->test_id = i; break; } } } if (cm_utf->test_id == CM_UTF_ID_MAX) { mlme_err("Invalid Test Case"); return; } mlme_err("Test ID: %d", cm_utf->test_id); switch (cm_utf->test_id) { case CM_UTF_ID_CONNECT_SUCCESS: case CM_UTF_ID_PEER_CREATE_FAILURE: case CM_UTF_ID_PEER_CREATE_TIMEOUT: case CM_UTF_ID_AUTH_FAILURE: case CM_UTF_ID_AUTH_TIMEOUT: case CM_UTF_ID_ASSOC_FAILURE: case CM_UTF_ID_ASSOC_TIMEOUT: case CM_UTF_ID_CONNECT_SCAN_FAILURE: case CM_UTF_ID_CONNECT_SER_TIMEOUT: case CM_UTF_ID_CONNECT_SER_FAILED: cm_utf->req.vdev_id = wlan_vdev_get_id(cm_utf->vdev); cm_utf->req.source = CM_OSIF_CONNECT; ucfg_cm_start_connect(cm_utf->vdev, &cm_utf->req); break; case CM_UTF_ID_DISCONNECT_SUCCESS: case CM_UTF_ID_DISCONNECT_SER_TIMEOUT: case CM_UTF_ID_DISCONNECT_SER_FAILED: case CM_UTF_ID_PEER_DELETE_TIMEOUT: token = qdf_str_sep(&buff, " "); if (!token) { mlme_err("Provide reason code for disconnect"); cm_utf->test_id = CM_UTF_ID_MAX; return; } else { ret = qdf_kstrtoint(token, 0, &reason); if (ret) { mlme_err("Invalid disconnect reason"); cm_utf->test_id = CM_UTF_ID_MAX; return; } } mlme_err("Disconnect reason %d", reason); req.vdev_id = wlan_vdev_get_id(cm_utf->vdev); req.source = CM_OSIF_DISCONNECT; req.reason_code = reason; ucfg_cm_start_disconnect(cm_utf->vdev, &req); break; default: mlme_err("Invalid test ID"); cm_utf->test_id = CM_UTF_ID_MAX; qdf_timer_stop(&cm_utf->cm_utf_test_timer); break; } if (cm_utf->test_id == CM_UTF_ID_PEER_DELETE_TIMEOUT || cm_utf->test_id == CM_UTF_ID_PEER_CREATE_TIMEOUT || cm_utf->test_id == CM_UTF_ID_DISCONNECT_SER_TIMEOUT || cm_utf->test_id == CM_UTF_ID_CONNECT_SER_TIMEOUT) qdf_timer_start(&cm_utf->cm_utf_test_timer, 70000); else qdf_timer_start(&cm_utf->cm_utf_test_timer, 10000); } int wlan_cm_utf_cm_test_id_show(qdf_debugfs_file_t m, void *v) { uint8_t i; mlme_nofl_err("Usage:"); for (i = 0; i < CM_UTF_ID_MAX; i++) { switch (i) { case CM_UTF_ID_DISCONNECT_SUCCESS: case CM_UTF_ID_DISCONNECT_SER_TIMEOUT: case CM_UTF_ID_DISCONNECT_SER_FAILED: case CM_UTF_ID_PEER_DELETE_TIMEOUT: mlme_nofl_err("%22s ", cm_utf_test_names[i]); break; default: mlme_nofl_err("%22s", cm_utf_test_names[i]); break; } } return 0; } ssize_t wlan_cm_utf_cm_test_id_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct wlan_cm_utf *cm_utf = ((struct seq_file *)file->private_data)->private; char *locbuf; if ((!buf) || (count <= 0)) return -EFAULT; locbuf = (char *)qdf_mem_malloc(count + 1); if (!locbuf) return -EFAULT; qdf_mem_zero(locbuf, count + 1); if (copy_from_user(locbuf, buf, count)) return -EFAULT; mlme_err("CM Start Test"); wlan_cm_utf_start_test(cm_utf, locbuf); qdf_mem_free(locbuf); return count; } #endif