/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2021 NXP */ #include #include #include #include #include #include #include #include #include #include #include #include #include "enet_pmd_logs.h" #include "enet_uio.h" static struct uio_job enetfec_uio_job; static int enetfec_count; /** @brief Checks if a file name contains a certain substring. * This function assumes a filename format of: [text][number]. * @param [in] filename File name * @param [in] match String to match in file name * * @retval true if file name matches the criteria * @retval false if file name does not match the criteria */ static bool file_name_match_extract(const char filename[], const char match[]) { char *substr = NULL; substr = strstr(filename, match); if (substr == NULL) return false; return true; } /* * @brief Reads first line from a file. * Composes file name as: root/subdir/filename * * @param [in] root Root path * @param [in] subdir Subdirectory name * @param [in] filename File name * @param [out] line The first line read from file. * * @retval 0 for success * @retval other value for error */ static int file_read_first_line(const char root[], const char subdir[], const char filename[], char *line) { char absolute_file_name[FEC_UIO_MAX_ATTR_FILE_NAME]; int fd = 0, ret = 0; /*compose the file name: root/subdir/filename */ memset(absolute_file_name, 0, sizeof(absolute_file_name)); snprintf(absolute_file_name, FEC_UIO_MAX_ATTR_FILE_NAME, "%s/%s/%s", root, subdir, filename); fd = open(absolute_file_name, O_RDONLY); if (fd <= 0) ENETFEC_PMD_ERR("Error opening file %s", absolute_file_name); /* read UIO device name from first line in file */ ret = read(fd, line, FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH); if (ret <= 0) { ENETFEC_PMD_ERR("Error reading file %s", absolute_file_name); return ret; } close(fd); /* NULL-ify string */ line[ret] = '\0'; return 0; } /* * @brief Maps rx-tx bd range assigned for a bd ring. * * @param [in] uio_device_fd UIO device file descriptor * @param [in] uio_device_id UIO device id * @param [in] uio_map_id UIO allows maximum 5 different mapping for each device. Maps start with id 0. * @param [out] map_size Map size. * @param [out] map_addr Map physical address * * @retval NULL if failed to map registers * @retval Virtual address for mapped register address range */ static void * uio_map_mem(int uio_device_fd, int uio_device_id, int uio_map_id, int *map_size, uint64_t *map_addr) { void *mapped_address = NULL; unsigned int uio_map_size = 0; unsigned int uio_map_p_addr = 0; char uio_sys_root[FEC_UIO_MAX_ATTR_FILE_NAME]; char uio_sys_map_subdir[FEC_UIO_MAX_ATTR_FILE_NAME]; char uio_map_size_str[FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH + 1]; char uio_map_p_addr_str[32]; int ret = 0; /* compose the file name: root/subdir/filename */ memset(uio_sys_root, 0, sizeof(uio_sys_root)); memset(uio_sys_map_subdir, 0, sizeof(uio_sys_map_subdir)); memset(uio_map_size_str, 0, sizeof(uio_map_size_str)); memset(uio_map_p_addr_str, 0, sizeof(uio_map_p_addr_str)); /* Compose string: /sys/class/uio/uioX */ snprintf(uio_sys_root, sizeof(uio_sys_root), "%s/%s%d", FEC_UIO_DEVICE_SYS_ATTR_PATH, "uio", uio_device_id); /* Compose string: maps/mapY */ snprintf(uio_sys_map_subdir, sizeof(uio_sys_map_subdir), "%s%d", FEC_UIO_DEVICE_SYS_MAP_ATTR, uio_map_id); /* Read first (and only) line from file * /sys/class/uio/uioX/maps/mapY/size */ ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, "size", uio_map_size_str); if (ret < 0) { ENETFEC_PMD_ERR("file_read_first_line() failed"); return NULL; } ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, "addr", uio_map_p_addr_str); if (ret < 0) { ENETFEC_PMD_ERR("file_read_first_line() failed"); return NULL; } /* Read mapping size and physical address expressed in hexa(base 16) */ uio_map_size = strtol(uio_map_size_str, NULL, 16); uio_map_p_addr = strtol(uio_map_p_addr_str, NULL, 16); if (uio_map_id == 0) { /* Map the register address in user space when map_id is 0 */ mapped_address = mmap(0 /*dynamically choose virtual address */, uio_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, uio_device_fd, 0); } else { /* Map the BD memory in user space */ mapped_address = mmap(NULL, uio_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, uio_device_fd, (1 * MAP_PAGE_SIZE)); } if (mapped_address == MAP_FAILED) { ENETFEC_PMD_ERR("Failed to map! errno = %d uio job fd = %d," "uio device id = %d, uio map id = %d", errno, uio_device_fd, uio_device_id, uio_map_id); return NULL; } /* Save the map size to use it later on for munmap-ing */ *map_size = uio_map_size; *map_addr = uio_map_p_addr; ENETFEC_PMD_INFO("UIO dev[%d] mapped region [id =%d] size 0x%x at %p", uio_device_id, uio_map_id, uio_map_size, mapped_address); return mapped_address; } int config_enetfec_uio(struct enetfec_private *fep) { char uio_device_file_name[32]; struct uio_job *uio_job = NULL; /* Mapping is done only one time */ if (enetfec_count > 0) { ENETFEC_PMD_INFO("Mapped!\n"); return 0; } uio_job = &enetfec_uio_job; /* Find UIO device created by ENETFEC-UIO kernel driver */ memset(uio_device_file_name, 0, sizeof(uio_device_file_name)); snprintf(uio_device_file_name, sizeof(uio_device_file_name), "%s%d", FEC_UIO_DEVICE_FILE_NAME, uio_job->uio_minor_number); /* Open device file */ uio_job->uio_fd = open(uio_device_file_name, O_RDWR); if (uio_job->uio_fd < 0) { ENETFEC_PMD_WARN("Unable to open ENETFEC_UIO file\n"); return -1; } ENETFEC_PMD_INFO("US_UIO: Open device(%s) file with uio_fd = %d", uio_device_file_name, uio_job->uio_fd); fep->hw_baseaddr_v = uio_map_mem(uio_job->uio_fd, uio_job->uio_minor_number, FEC_UIO_REG_MAP_ID, &uio_job->map_size, &uio_job->map_addr); if (fep->hw_baseaddr_v == NULL) return -ENOMEM; fep->hw_baseaddr_p = uio_job->map_addr; fep->reg_size = uio_job->map_size; fep->bd_addr_v = uio_map_mem(uio_job->uio_fd, uio_job->uio_minor_number, FEC_UIO_BD_MAP_ID, &uio_job->map_size, &uio_job->map_addr); if (fep->hw_baseaddr_v == NULL) return -ENOMEM; fep->bd_addr_p = (uint32_t)uio_job->map_addr; fep->bd_size = uio_job->map_size; enetfec_count++; return 0; } int enetfec_configure(void) { char uio_name[32]; int uio_minor_number = -1; int ret; DIR *d = NULL; struct dirent *dir; d = opendir(FEC_UIO_DEVICE_SYS_ATTR_PATH); if (d == NULL) { ENETFEC_PMD_ERR("\nError opening directory '%s': %s\n", FEC_UIO_DEVICE_SYS_ATTR_PATH, strerror(errno)); return -1; } /* Iterate through all subdirs */ while ((dir = readdir(d)) != NULL) { if (!strncmp(dir->d_name, ".", 1) || !strncmp(dir->d_name, "..", 2)) continue; if (file_name_match_extract(dir->d_name, "uio")) { /* * As substring was found in * read number following substring in */ ret = sscanf(dir->d_name + strlen("uio"), "%d", &uio_minor_number); if (ret < 0) ENETFEC_PMD_ERR("Error: not find minor number\n"); /* * Open file uioX/name and read first line which * contains the name for the device. Based on the * name check if this UIO device is for enetfec. */ memset(uio_name, 0, sizeof(uio_name)); ret = file_read_first_line(FEC_UIO_DEVICE_SYS_ATTR_PATH, dir->d_name, "name", uio_name); if (ret != 0) { ENETFEC_PMD_INFO("file_read_first_line failed\n"); closedir(d); return -1; } if (file_name_match_extract(uio_name, FEC_UIO_DEVICE_NAME)) { enetfec_uio_job.uio_minor_number = uio_minor_number; ENETFEC_PMD_INFO("enetfec device uio name: %s", uio_name); } } } closedir(d); return 0; } void enetfec_cleanup(struct enetfec_private *fep) { munmap(fep->hw_baseaddr_v, fep->cbus_size); }