// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hw_mgmt.h" #include "hinic_nic_dev.h" #include "hinic_lld.h" #include "hinic_dbgtool_knl.h" struct ffm_intr_info { u8 node_id; /* error level of the interrupt source */ u8 err_level; /* Classification by interrupt source properties */ u16 err_type; u32 err_csr_addr; u32 err_csr_value; }; #define DBGTOOL_MSG_MAX_SIZE 2048ULL #define HINIC_SELF_CMD_UP2PF_FFM 0x26 void *g_card_node_array[MAX_CARD_NUM] = {0}; void *g_card_vir_addr[MAX_CARD_NUM] = {0}; u64 g_card_phy_addr[MAX_CARD_NUM] = {0}; /* lock for g_card_vir_addr */ struct mutex g_addr_lock; int card_id; /* dbgtool character device name, class name, dev path */ #define CHR_DEV_DBGTOOL "dbgtool_chr_dev" #define CLASS_DBGTOOL "dbgtool_class" #define DBGTOOL_DEV_PATH "/dev/dbgtool_chr_dev" struct dbgtool_k_glb_info { struct semaphore dbgtool_sem; struct ffm_record_info *ffm; }; dev_t dbgtool_dev_id; /* device id */ struct cdev dbgtool_chr_dev; /* struct of char device */ /*lint -save -e104 -e808*/ struct class *dbgtool_d_class; /* struct of char class */ /*lint -restore*/ int g_dbgtool_init_flag; int g_dbgtool_ref_cnt; static int dbgtool_knl_open(struct inode *pnode, struct file *pfile) { return 0; } static int dbgtool_knl_release(struct inode *pnode, struct file *pfile) { return 0; } static ssize_t dbgtool_knl_read(struct file *pfile, char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static ssize_t dbgtool_knl_write(struct file *pfile, const char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static bool is_valid_phy_addr(u64 offset) { int i; for (i = 0; i < MAX_CARD_NUM; i++) { if (offset == g_card_phy_addr[i]) return true; } return false; } int hinic_mem_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long vmsize = vma->vm_end - vma->vm_start; phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; phys_addr_t phy_addr; if (vmsize > (PAGE_SIZE * (1 << DBGTOOL_PAGE_ORDER))) { pr_err("Map size = %lu is bigger than alloc\n", vmsize); return -EAGAIN; } if (offset && !is_valid_phy_addr((u64)offset) && !hinic_is_valid_bar_addr((u64)offset)) { pr_err("offset is invalid"); return -EAGAIN; } /* old version of tool set vma->vm_pgoff to 0 */ phy_addr = offset ? offset : g_card_phy_addr[card_id]; if (!phy_addr) { pr_err("Card_id = %d physical address is 0\n", card_id); return -EAGAIN; } if (remap_pfn_range(vma, vma->vm_start, (phy_addr >> PAGE_SHIFT), vmsize, vma->vm_page_prot)) return -EAGAIN; return 0; } /** * dbgtool_knl_api_cmd_read - used for read operations * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ long dbgtool_knl_api_cmd_read(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; u8 *cmd; u16 size; void *ack; u16 ack_size; u32 pf_id; void *hwdev; pf_id = para->param.api_rd.pf_id; if (pf_id >= 16) { pr_err("PF id(0x%x) too big\n", pf_id); return -EFAULT; } /* obtaining pf_id chipif pointer */ hwdev = g_func_handle_array[pf_id]; if (!hwdev) { pr_err("PF id(0x%x) handle null in api cmd read\n", pf_id); return -EFAULT; } /* alloc cmd and ack memory */ size = para->param.api_rd.size; if (para->param.api_rd.size == 0 || size > DBGTOOL_MSG_MAX_SIZE) { pr_err("Read cmd size invalid or more than 2M\n"); return -EINVAL; } cmd = kzalloc((unsigned long long)size, GFP_KERNEL); if (!cmd) { pr_err("Alloc read cmd mem fail\n"); return -ENOMEM; } ack_size = para->param.api_rd.ack_size; if (para->param.api_rd.ack_size == 0) { pr_err("Read cmd ack size is 0\n"); ret = -ENOMEM; goto alloc_ack_mem_fail; } ack = kzalloc((unsigned long long)ack_size, GFP_KERNEL); if (!ack) { pr_err("Alloc read ack mem fail\n"); ret = -ENOMEM; goto alloc_ack_mem_fail; } /* cmd content copied from user-mode */ if (copy_from_user(cmd, para->param.api_rd.cmd, (unsigned long)size)) { pr_err("Copy cmd from user fail\n"); ret = -EFAULT; goto copy_user_cmd_fail; } /* Invoke the api cmd interface read content*/ ret = hinic_api_cmd_read_ack(hwdev, para->param.api_rd.dest, cmd, size, ack, ack_size); if (ret) { pr_err("Api send single cmd ack fail!\n"); goto api_rd_fail; } /* Copy the contents of the ack to the user state */ if (copy_to_user(para->param.api_rd.ack, ack, ack_size)) { pr_err("Copy ack to user fail\n"); ret = -EFAULT; } api_rd_fail: copy_user_cmd_fail: kfree(ack); alloc_ack_mem_fail: kfree(cmd); return ret; } /** * dbgtool_knl_api_cmd_write - used for write operations * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ long dbgtool_knl_api_cmd_write(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; u8 *cmd; u16 size; u32 pf_id; void *hwdev; pf_id = para->param.api_wr.pf_id; if (pf_id >= 16) { pr_err("PF id(0x%x) too big\n", pf_id); return -EFAULT; } /* obtaining chipif pointer according to pf_id */ hwdev = g_func_handle_array[pf_id]; if (!hwdev) { pr_err("PF id(0x%x) handle null\n", pf_id); return -EFAULT; } /* alloc cmd memory */ size = para->param.api_wr.size; if (para->param.api_wr.size == 0 || size > DBGTOOL_MSG_MAX_SIZE) { pr_err("Write cmd size invalid or more than 2M\n"); return -EINVAL; } cmd = kzalloc((unsigned long long)size, GFP_KERNEL); if (!cmd) { pr_err("Alloc write cmd mem fail\n"); return -ENOMEM; } /* cmd content copied from user-mode */ if (copy_from_user(cmd, para->param.api_wr.cmd, (unsigned long)size)) { pr_err("Copy cmd from user fail\n"); ret = -EFAULT; goto copy_user_cmd_fail; } /* api cmd interface is invoked to write the content */ ret = hinic_api_cmd_write_nack(hwdev, para->param.api_wr.dest, cmd, size); if (ret) pr_err("Api send single cmd nack fail\n"); copy_user_cmd_fail: kfree(cmd); return ret; } void chipif_get_all_pf_dev_info(struct pf_dev_info *dev_info, int card_idx, void **g_func_handle_array) { u32 func_idx; struct hinic_hwdev *hwdev; if (!dev_info) { pr_err("Params error!\n"); return; } /* pf at most 16 */ for (func_idx = 0; func_idx < 16; func_idx++) { hwdev = (struct hinic_hwdev *)g_func_handle_array[func_idx]; dev_info[func_idx].phy_addr = g_card_phy_addr[card_idx]; if (!hwdev) { dev_info[func_idx].bar0_size = 0; dev_info[func_idx].bus = 0; dev_info[func_idx].slot = 0; dev_info[func_idx].func = 0; } else { dev_info[func_idx].bar0_size = pci_resource_len (((struct pci_dev *)hwdev->pcidev_hdl), 0); dev_info[func_idx].bus = ((struct pci_dev *) hwdev->pcidev_hdl)->bus->number; dev_info[func_idx].slot = PCI_SLOT(((struct pci_dev *)hwdev->pcidev_hdl) ->devfn); dev_info[func_idx].func = PCI_FUNC(((struct pci_dev *)hwdev->pcidev_hdl) ->devfn); } } } /** * dbgtool_knl_pf_dev_info_get - Obtain the pf sdk_info * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ long dbgtool_knl_pf_dev_info_get(struct dbgtool_param *para, void **g_func_handle_array) { struct pf_dev_info dev_info[16] = { {0} }; unsigned char *tmp; int i; mutex_lock(&g_addr_lock); if (!g_card_vir_addr[card_id]) { g_card_vir_addr[card_id] = (void *)__get_free_pages(GFP_KERNEL, DBGTOOL_PAGE_ORDER); if (!g_card_vir_addr[card_id]) { pr_err("Alloc dbgtool api chain fail!\n"); mutex_unlock(&g_addr_lock); return -EFAULT; } memset(g_card_vir_addr[card_id], 0, PAGE_SIZE * (1 << DBGTOOL_PAGE_ORDER)); g_card_phy_addr[card_id] = virt_to_phys(g_card_vir_addr[card_id]); if (!g_card_phy_addr[card_id]) { pr_err("phy addr for card %d is 0\n", card_id); free_pages((unsigned long)g_card_vir_addr[card_id], DBGTOOL_PAGE_ORDER); g_card_vir_addr[card_id] = NULL; mutex_unlock(&g_addr_lock); return -EFAULT; } tmp = g_card_vir_addr[card_id]; for (i = 0; i < (1 << DBGTOOL_PAGE_ORDER); i++) { SetPageReserved(virt_to_page(tmp)); tmp += PAGE_SIZE; } } mutex_unlock(&g_addr_lock); chipif_get_all_pf_dev_info(dev_info, card_id, g_func_handle_array); /* Copy the dev_info to user mode */ if (copy_to_user(para->param.dev_info, dev_info, (unsigned int)sizeof(dev_info))) { pr_err("Copy dev_info to user fail\n"); return -EFAULT; } return 0; } /** * dbgtool_knl_ffm_info_rd - Read ffm information * @para: the dbgtool parameter * @dbgtool_info: the dbgtool info * Return: 0 - success, negative - failure */ long dbgtool_knl_ffm_info_rd(struct dbgtool_param *para, struct dbgtool_k_glb_info *dbgtool_info) { /* Copy the ffm_info to user mode */ if (copy_to_user(para->param.ffm_rd, dbgtool_info->ffm, (unsigned int)sizeof(struct ffm_record_info))) { pr_err("Copy ffm_info to user fail\n"); return -EFAULT; } return 0; } /** * dbgtool_knl_ffm_info_clr - Clear FFM information * @para: unused * @dbgtool_info: the dbgtool info */ void dbgtool_knl_ffm_info_clr(struct dbgtool_param *para, struct dbgtool_k_glb_info *dbgtool_info) { dbgtool_info->ffm->ffm_num = 0; } /** * dbgtool_knl_msg_to_up - After receiving dbgtool command sends a message to uP * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ long dbgtool_knl_msg_to_up(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; void *buf_in; void *buf_out; u16 out_size; u8 pf_id; if (para->param.msg2up.in_size > DBGTOOL_MSG_MAX_SIZE) { pr_err("User data(%d) more than 2KB\n", para->param.msg2up.in_size); return -EFAULT; } pf_id = para->param.msg2up.pf_id; /* pf at most 16 */ if (pf_id >= 16) { pr_err("PF id(0x%x) too big in message to mgmt\n", pf_id); return -EFAULT; } if (!g_func_handle_array[pf_id]) { pr_err("PF id(0x%x) handle null in message to mgmt\n", pf_id); return -EFAULT; } /* alloc buf_in and buf_out memory, apply for 2K */ buf_in = kzalloc(DBGTOOL_MSG_MAX_SIZE, GFP_KERNEL); if (!buf_in) { pr_err("Alloc buf_in mem fail\n"); return -ENOMEM; } buf_out = kzalloc(DBGTOOL_MSG_MAX_SIZE, 0); if (!buf_out) { pr_err("Alloc buf_out mem fail\n"); ret = -ENOMEM; goto alloc_buf_out_mem_fail; } /* copy buf_in from the user state */ if (copy_from_user(buf_in, para->param.msg2up.buf_in, (unsigned long)para->param.msg2up.in_size)) { pr_err("Copy buf_in from user fail\n"); ret = -EFAULT; goto copy_user_buf_in_fail; } out_size = DBGTOOL_MSG_MAX_SIZE; /* Invoke the pf2up communication interface */ ret = hinic_msg_to_mgmt_sync(g_func_handle_array[pf_id], para->param.msg2up.mod, para->param.msg2up.cmd, buf_in, para->param.msg2up.in_size, buf_out, &out_size, 0); if (ret) goto msg_2_up_fail; /* Copy the out_size and buf_out content to user mode */ if (copy_to_user(para->param.msg2up.out_size, &out_size, (unsigned int)sizeof(out_size))) { pr_err("Copy out_size to user fail\n"); ret = -EFAULT; goto copy_out_size_fail; } if (copy_to_user(para->param.msg2up.buf_out, buf_out, out_size)) { pr_err("Copy buf_out to user fail\n"); ret = -EFAULT; } copy_out_size_fail: msg_2_up_fail: copy_user_buf_in_fail: kfree(buf_out); alloc_buf_out_mem_fail: kfree(buf_in); return ret; } long dbgtool_knl_free_mem(int id) { unsigned char *tmp; int i; mutex_lock(&g_addr_lock); if (!g_card_vir_addr[id]) { mutex_unlock(&g_addr_lock); return 0; } tmp = g_card_vir_addr[id]; for (i = 0; i < (1 << DBGTOOL_PAGE_ORDER); i++) { ClearPageReserved(virt_to_page(tmp)); tmp += PAGE_SIZE; } free_pages((unsigned long)g_card_vir_addr[id], DBGTOOL_PAGE_ORDER); g_card_vir_addr[id] = NULL; g_card_phy_addr[id] = 0; mutex_unlock(&g_addr_lock); return 0; } /*lint -save -e771 -e794*/ /** * dbgtool_knl_unlocked_ioctl - dbgtool ioctl entry * @pfile: the pointer to file * @cmd: the command type */ long dbgtool_knl_unlocked_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) { long ret = 0; unsigned int real_cmd; struct dbgtool_param param; struct dbgtool_k_glb_info *dbgtool_info; struct card_node *card_info = NULL; int i; (void)memset(¶m, 0, sizeof(param)); if (copy_from_user(¶m, (void *)arg, sizeof(param))) { pr_err("Copy param from user fail\n"); return -EFAULT; } param.chip_name[IFNAMSIZ - 1] = '\0'; for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_card_node_array[i]; if (!card_info) continue; if (!strncmp(param.chip_name, card_info->chip_name, IFNAMSIZ)) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Can't find this card %s\n", param.chip_name); return -EFAULT; } card_id = i; dbgtool_info = (struct dbgtool_k_glb_info *)card_info->dbgtool_info; down(&dbgtool_info->dbgtool_sem); real_cmd = _IOC_NR(cmd); switch (real_cmd) { case DBGTOOL_CMD_API_RD: ret = dbgtool_knl_api_cmd_read(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_API_WR: ret = dbgtool_knl_api_cmd_write(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_FFM_RD: ret = dbgtool_knl_ffm_info_rd(¶m, dbgtool_info); break; case DBGTOOL_CMD_FFM_CLR: dbgtool_knl_ffm_info_clr(¶m, dbgtool_info); break; case DBGTOOL_CMD_PF_DEV_INFO_GET: ret = dbgtool_knl_pf_dev_info_get(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_MSG_2_UP: ret = dbgtool_knl_msg_to_up(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_FREE_MEM: ret = dbgtool_knl_free_mem(i); break; default: pr_err("Dbgtool cmd(x%x) not support now\n", real_cmd); ret = -EFAULT; } up(&dbgtool_info->dbgtool_sem); return ret; } /** * ffm_intr_msg_record - FFM interruption records sent up * @handle: the function handle * @buf_in: the pointer to input buffer * @buf_out: the pointer to outputput buffer */ void ffm_intr_msg_record(void *handle, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct dbgtool_k_glb_info *dbgtool_info; struct ffm_intr_info *intr; u32 ffm_idx; struct timex txc; struct rtc_time rctm; struct card_node *card_info = NULL; bool flag = false; int i, j; for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_card_node_array[i]; if (!card_info) continue; for (j = 0; j < MAX_FUNCTION_NUM; j++) { if (handle == card_info->func_handle_array[j]) { flag = true; break; } } if (flag) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Id(%d) cant find this card\n", i); return; } dbgtool_info = (struct dbgtool_k_glb_info *)card_info->dbgtool_info; if (!dbgtool_info) { pr_err("Dbgtool info is null\n"); return; } intr = (struct ffm_intr_info *)buf_in; if (!dbgtool_info->ffm) return; ffm_idx = dbgtool_info->ffm->ffm_num; if (ffm_idx < FFM_RECORD_NUM_MAX) { pr_info("%s: recv intr, ffm_idx: %d\n", __func__, ffm_idx); dbgtool_info->ffm->ffm[ffm_idx].node_id = intr->node_id; dbgtool_info->ffm->ffm[ffm_idx].err_level = intr->err_level; dbgtool_info->ffm->ffm[ffm_idx].err_type = intr->err_type; dbgtool_info->ffm->ffm[ffm_idx].err_csr_addr = intr->err_csr_addr; dbgtool_info->ffm->ffm[ffm_idx].err_csr_value = intr->err_csr_value; /* Obtain the current UTC time */ do_gettimeofday(&txc.time); /* Calculate the time in date value to tm */ rtc_time_to_tm((unsigned long)txc.time.tv_sec + 60 * 60 * 8, &rctm); /* tm_year starts from 1900; 0->1900, 1->1901, and so on */ dbgtool_info->ffm->ffm[ffm_idx].year = (u16)(rctm.tm_year + 1900); /* tm_mon starts from 0, 0 indicates January, and so on */ dbgtool_info->ffm->ffm[ffm_idx].mon = (u8)rctm.tm_mon + 1; dbgtool_info->ffm->ffm[ffm_idx].mday = (u8)rctm.tm_mday; dbgtool_info->ffm->ffm[ffm_idx].hour = (u8)rctm.tm_hour; dbgtool_info->ffm->ffm[ffm_idx].min = (u8)rctm.tm_min; dbgtool_info->ffm->ffm[ffm_idx].sec = (u8)rctm.tm_sec; dbgtool_info->ffm->ffm_num++; } } /*lint -restore*/ /*lint -save -e785 -e438*/ static const struct file_operations dbgtool_file_operations = { .owner = THIS_MODULE, .open = dbgtool_knl_open, .release = dbgtool_knl_release, .read = dbgtool_knl_read, .write = dbgtool_knl_write, .unlocked_ioctl = dbgtool_knl_unlocked_ioctl, .mmap = hinic_mem_mmap, }; /** * dbgtool_knl_init - dbgtool character device init * @hwdev: the pointer to hardware device * @chip_node: the pointer to card node * Return: 0 - success, negative - failure */ int dbgtool_knl_init(void *vhwdev, void *chip_node) { int ret = 0; int id; struct dbgtool_k_glb_info *dbgtool_info; struct device *pdevice; struct card_node *chip_info = (struct card_node *)chip_node; struct hinic_hwdev *hwdev = vhwdev; if (hinic_func_type(hwdev) == TYPE_VF) return 0; ret = sysfs_create_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); if (ret) { pr_err("Failed to sysfs create file\n"); return ret; } chip_info->func_handle_array[hinic_global_func_id(hwdev)] = hwdev; hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_SELF_CMD_UP2PF_FFM, ffm_intr_msg_record); if (chip_info->dbgtool_info) { chip_info->func_num++; return 0; } dbgtool_info = (struct dbgtool_k_glb_info *) kzalloc(sizeof(struct dbgtool_k_glb_info), GFP_KERNEL); if (!dbgtool_info) { pr_err("Failed to allocate dbgtool_info\n"); ret = -EFAULT; goto dbgtool_info_fail; } chip_info->dbgtool_info = dbgtool_info; /* FFM init */ dbgtool_info->ffm = (struct ffm_record_info *) kzalloc(sizeof(struct ffm_record_info), GFP_KERNEL); if (!dbgtool_info->ffm) { pr_err("Failed to allocate cell contexts for a chain\n"); ret = -EFAULT; goto dbgtool_info_ffm_fail; } sema_init(&dbgtool_info->dbgtool_sem, 1); ret = sscanf(chip_info->chip_name, HINIC_CHIP_NAME "%d", &id); if (ret < 0) { pr_err("Failed to get hinic id\n"); goto sscanf_chdev_fail; } g_card_node_array[id] = chip_info; chip_info->func_num++; if (g_dbgtool_init_flag) { g_dbgtool_ref_cnt++; /* already initialized */ return 0; } /*alloc device id*/ ret = alloc_chrdev_region(&(dbgtool_dev_id), 0, 1, CHR_DEV_DBGTOOL); if (ret) { pr_err("Alloc dbgtool chrdev region fail, ret=0x%x\n", ret); goto alloc_chdev_fail; } /*init device*/ cdev_init(&(dbgtool_chr_dev), &dbgtool_file_operations); /*add device*/ ret = cdev_add(&(dbgtool_chr_dev), dbgtool_dev_id, 1); if (ret) { pr_err("Add dgbtool dev fail, ret=0x%x\n", ret); goto cdev_add_fail; } /*lint -save -e160*/ dbgtool_d_class = class_create(THIS_MODULE, CLASS_DBGTOOL); /*lint -restore*/ if (IS_ERR(dbgtool_d_class)) { pr_err("Create dgbtool class fail\n"); ret = -EFAULT; goto cls_create_fail; } /* Export device information to user space * (/sys/class/class name/device name) */ pdevice = device_create(dbgtool_d_class, NULL, dbgtool_dev_id, NULL, CHR_DEV_DBGTOOL); if (IS_ERR(pdevice)) { pr_err("Create dgbtool device fail\n"); ret = -EFAULT; goto dev_create_fail; } g_dbgtool_init_flag = 1; g_dbgtool_ref_cnt = 1; mutex_init(&g_addr_lock); return 0; dev_create_fail: class_destroy(dbgtool_d_class); cls_create_fail: cdev_del(&(dbgtool_chr_dev)); cdev_add_fail: unregister_chrdev_region(dbgtool_dev_id, 1); alloc_chdev_fail: g_card_node_array[id] = NULL; sscanf_chdev_fail: kfree(dbgtool_info->ffm); dbgtool_info_ffm_fail: kfree(dbgtool_info); dbgtool_info = NULL; chip_info->dbgtool_info = NULL; dbgtool_info_fail: hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_SELF_CMD_UP2PF_FFM); chip_info->func_handle_array[hinic_global_func_id(hwdev)] = NULL; sysfs_remove_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); return ret; } /** * dbgtool_knl_deinit - dbgtool character device deinit * @hwdev: the pointer to hardware device * @chip_node: the pointer to card node */ void dbgtool_knl_deinit(void *vhwdev, void *chip_node) { struct dbgtool_k_glb_info *dbgtool_info; struct card_node *chip_info = (struct card_node *)chip_node; int id; int err; struct hinic_hwdev *hwdev = vhwdev; if (hinic_func_type(hwdev) == TYPE_VF) return; hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_SELF_CMD_UP2PF_FFM); chip_info->func_handle_array[hinic_global_func_id(hwdev)] = NULL; sysfs_remove_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); chip_info->func_num--; if (chip_info->func_num) return; err = sscanf(chip_info->chip_name, HINIC_CHIP_NAME "%d", &id); if (err < 0) pr_err("Failed to get hinic id\n"); g_card_node_array[id] = NULL; dbgtool_info = chip_info->dbgtool_info; /* FFM deinit */ kfree(dbgtool_info->ffm); dbgtool_info->ffm = NULL; kfree(dbgtool_info); chip_info->dbgtool_info = NULL; (void)dbgtool_knl_free_mem(id); if (g_dbgtool_init_flag) { if ((--g_dbgtool_ref_cnt)) return; } if (!dbgtool_d_class) return; device_destroy(dbgtool_d_class, dbgtool_dev_id); class_destroy(dbgtool_d_class); dbgtool_d_class = NULL; cdev_del(&(dbgtool_chr_dev)); unregister_chrdev_region(dbgtool_dev_id, 1); g_dbgtool_init_flag = 0; } /*lint -restore*/