1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright 2021 NXP 3 */ 4 5 #include <stdbool.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <unistd.h> 9 #include <stdlib.h> 10 #include <dirent.h> 11 #include <string.h> 12 #include <sys/mman.h> 13 #include <errno.h> 14 #include <fcntl.h> 15 16 #include <rte_common.h> 17 #include <rte_malloc.h> 18 #include "enet_pmd_logs.h" 19 #include "enet_uio.h" 20 21 static struct uio_job enetfec_uio_job; 22 static int enetfec_count; 23 24 /** @brief Checks if a file name contains a certain substring. 25 * This function assumes a filename format of: [text][number]. 26 * @param [in] filename File name 27 * @param [in] match String to match in file name 28 * 29 * @retval true if file name matches the criteria 30 * @retval false if file name does not match the criteria 31 */ 32 static bool 33 file_name_match_extract(const char filename[], const char match[]) 34 { 35 char *substr = NULL; 36 37 substr = strstr(filename, match); 38 if (substr == NULL) 39 return false; 40 41 return true; 42 } 43 44 /* 45 * @brief Reads first line from a file. 46 * Composes file name as: root/subdir/filename 47 * 48 * @param [in] root Root path 49 * @param [in] subdir Subdirectory name 50 * @param [in] filename File name 51 * @param [out] line The first line read from file. 52 * 53 * @retval 0 for success 54 * @retval other value for error 55 */ 56 static int 57 file_read_first_line(const char root[], const char subdir[], 58 const char filename[], char *line) 59 { 60 char absolute_file_name[FEC_UIO_MAX_ATTR_FILE_NAME]; 61 int fd = 0, ret = 0; 62 63 /*compose the file name: root/subdir/filename */ 64 memset(absolute_file_name, 0, sizeof(absolute_file_name)); 65 snprintf(absolute_file_name, FEC_UIO_MAX_ATTR_FILE_NAME, 66 "%s/%s/%s", root, subdir, filename); 67 68 fd = open(absolute_file_name, O_RDONLY); 69 if (fd <= 0) 70 ENETFEC_PMD_ERR("Error opening file %s", absolute_file_name); 71 72 /* read UIO device name from first line in file */ 73 ret = read(fd, line, FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH); 74 if (ret <= 0) { 75 ENETFEC_PMD_ERR("Error reading file %s", absolute_file_name); 76 return ret; 77 } 78 close(fd); 79 80 /* NULL-ify string */ 81 line[ret] = '\0'; 82 83 return 0; 84 } 85 86 /* 87 * @brief Maps rx-tx bd range assigned for a bd ring. 88 * 89 * @param [in] uio_device_fd UIO device file descriptor 90 * @param [in] uio_device_id UIO device id 91 * @param [in] uio_map_id UIO allows maximum 5 different mapping for 92 each device. Maps start with id 0. 93 * @param [out] map_size Map size. 94 * @param [out] map_addr Map physical address 95 * 96 * @retval NULL if failed to map registers 97 * @retval Virtual address for mapped register address range 98 */ 99 static void * 100 uio_map_mem(int uio_device_fd, int uio_device_id, 101 int uio_map_id, int *map_size, uint64_t *map_addr) 102 { 103 void *mapped_address = NULL; 104 unsigned int uio_map_size = 0; 105 unsigned int uio_map_p_addr = 0; 106 char uio_sys_root[FEC_UIO_MAX_ATTR_FILE_NAME]; 107 char uio_sys_map_subdir[FEC_UIO_MAX_ATTR_FILE_NAME]; 108 char uio_map_size_str[FEC_UIO_MAX_DEVICE_FILE_NAME_LENGTH + 1]; 109 char uio_map_p_addr_str[32]; 110 int ret = 0; 111 112 /* compose the file name: root/subdir/filename */ 113 memset(uio_sys_root, 0, sizeof(uio_sys_root)); 114 memset(uio_sys_map_subdir, 0, sizeof(uio_sys_map_subdir)); 115 memset(uio_map_size_str, 0, sizeof(uio_map_size_str)); 116 memset(uio_map_p_addr_str, 0, sizeof(uio_map_p_addr_str)); 117 118 /* Compose string: /sys/class/uio/uioX */ 119 snprintf(uio_sys_root, sizeof(uio_sys_root), "%s/%s%d", 120 FEC_UIO_DEVICE_SYS_ATTR_PATH, "uio", uio_device_id); 121 /* Compose string: maps/mapY */ 122 snprintf(uio_sys_map_subdir, sizeof(uio_sys_map_subdir), "%s%d", 123 FEC_UIO_DEVICE_SYS_MAP_ATTR, uio_map_id); 124 125 /* Read first (and only) line from file 126 * /sys/class/uio/uioX/maps/mapY/size 127 */ 128 ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, 129 "size", uio_map_size_str); 130 if (ret < 0) { 131 ENETFEC_PMD_ERR("file_read_first_line() failed"); 132 return NULL; 133 } 134 ret = file_read_first_line(uio_sys_root, uio_sys_map_subdir, 135 "addr", uio_map_p_addr_str); 136 if (ret < 0) { 137 ENETFEC_PMD_ERR("file_read_first_line() failed"); 138 return NULL; 139 } 140 /* Read mapping size and physical address expressed in hexa(base 16) */ 141 uio_map_size = strtol(uio_map_size_str, NULL, 16); 142 uio_map_p_addr = strtol(uio_map_p_addr_str, NULL, 16); 143 144 if (uio_map_id == 0) { 145 /* Map the register address in user space when map_id is 0 */ 146 mapped_address = mmap(0 /*dynamically choose virtual address */, 147 uio_map_size, PROT_READ | PROT_WRITE, 148 MAP_SHARED, uio_device_fd, 0); 149 } else { 150 /* Map the BD memory in user space */ 151 mapped_address = mmap(NULL, uio_map_size, 152 PROT_READ | PROT_WRITE, 153 MAP_SHARED, uio_device_fd, (1 * MAP_PAGE_SIZE)); 154 } 155 156 if (mapped_address == MAP_FAILED) { 157 ENETFEC_PMD_ERR("Failed to map! errno = %d uio job fd = %d," 158 "uio device id = %d, uio map id = %d", errno, 159 uio_device_fd, uio_device_id, uio_map_id); 160 return NULL; 161 } 162 163 /* Save the map size to use it later on for munmap-ing */ 164 *map_size = uio_map_size; 165 *map_addr = uio_map_p_addr; 166 ENETFEC_PMD_INFO("UIO dev[%d] mapped region [id =%d] size 0x%x at %p", 167 uio_device_id, uio_map_id, uio_map_size, mapped_address); 168 169 return mapped_address; 170 } 171 172 int 173 config_enetfec_uio(struct enetfec_private *fep) 174 { 175 char uio_device_file_name[32]; 176 struct uio_job *uio_job = NULL; 177 178 /* Mapping is done only one time */ 179 if (enetfec_count > 0) { 180 ENETFEC_PMD_INFO("Mapped!"); 181 return 0; 182 } 183 184 uio_job = &enetfec_uio_job; 185 186 /* Find UIO device created by ENETFEC-UIO kernel driver */ 187 memset(uio_device_file_name, 0, sizeof(uio_device_file_name)); 188 snprintf(uio_device_file_name, sizeof(uio_device_file_name), "%s%d", 189 FEC_UIO_DEVICE_FILE_NAME, uio_job->uio_minor_number); 190 191 /* Open device file */ 192 uio_job->uio_fd = open(uio_device_file_name, O_RDWR); 193 if (uio_job->uio_fd < 0) { 194 ENETFEC_PMD_WARN("Unable to open ENETFEC_UIO file"); 195 return -1; 196 } 197 198 ENETFEC_PMD_INFO("US_UIO: Open device(%s) file with uio_fd = %d", 199 uio_device_file_name, uio_job->uio_fd); 200 201 fep->hw_baseaddr_v = uio_map_mem(uio_job->uio_fd, 202 uio_job->uio_minor_number, FEC_UIO_REG_MAP_ID, 203 &uio_job->map_size, &uio_job->map_addr); 204 if (fep->hw_baseaddr_v == NULL) 205 return -ENOMEM; 206 fep->hw_baseaddr_p = uio_job->map_addr; 207 fep->reg_size = uio_job->map_size; 208 209 fep->bd_addr_v = uio_map_mem(uio_job->uio_fd, 210 uio_job->uio_minor_number, FEC_UIO_BD_MAP_ID, 211 &uio_job->map_size, &uio_job->map_addr); 212 if (fep->hw_baseaddr_v == NULL) 213 return -ENOMEM; 214 fep->bd_addr_p = (uint32_t)uio_job->map_addr; 215 fep->bd_size = uio_job->map_size; 216 217 enetfec_count++; 218 219 return 0; 220 } 221 222 int 223 enetfec_configure(void) 224 { 225 char uio_name[32]; 226 int uio_minor_number = -1; 227 int ret; 228 DIR *d = NULL; 229 struct dirent *dir; 230 231 d = opendir(FEC_UIO_DEVICE_SYS_ATTR_PATH); 232 if (d == NULL) { 233 ENETFEC_PMD_ERR("Error opening directory '%s': %s", 234 FEC_UIO_DEVICE_SYS_ATTR_PATH, strerror(errno)); 235 return -1; 236 } 237 238 /* Iterate through all subdirs */ 239 while ((dir = readdir(d)) != NULL) { 240 if (!strncmp(dir->d_name, ".", 1) || 241 !strncmp(dir->d_name, "..", 2)) 242 continue; 243 244 if (file_name_match_extract(dir->d_name, "uio")) { 245 /* 246 * As substring <uio> was found in <d_name> 247 * read number following <uio> substring in <d_name> 248 */ 249 ret = sscanf(dir->d_name + strlen("uio"), "%d", 250 &uio_minor_number); 251 if (ret < 0) 252 ENETFEC_PMD_ERR("Error: not find minor number"); 253 /* 254 * Open file uioX/name and read first line which 255 * contains the name for the device. Based on the 256 * name check if this UIO device is for enetfec. 257 */ 258 memset(uio_name, 0, sizeof(uio_name)); 259 ret = file_read_first_line(FEC_UIO_DEVICE_SYS_ATTR_PATH, 260 dir->d_name, "name", uio_name); 261 if (ret != 0) { 262 ENETFEC_PMD_INFO("file_read_first_line failed"); 263 closedir(d); 264 return -1; 265 } 266 267 if (file_name_match_extract(uio_name, 268 FEC_UIO_DEVICE_NAME)) { 269 enetfec_uio_job.uio_minor_number = 270 uio_minor_number; 271 ENETFEC_PMD_INFO("enetfec device uio name: %s", 272 uio_name); 273 } 274 } 275 } 276 closedir(d); 277 return 0; 278 } 279 280 void 281 enetfec_cleanup(struct enetfec_private *fep) 282 { 283 munmap(fep->hw_baseaddr_v, fep->cbus_size); 284 } 285