rtapi_pci.cc
1 /******************************************************************** 2 * Description: rtapi_pci.c 3 * This file, 'rtapi_pci.c', implements the 4 * usermode PCI functions 5 * 6 * 7 * Copyright (C) 2009 - 2013 Michael Büsch <m AT bues DOT CH>, 8 * Charles Steinkuehler <charles AT steinkuehler DOT net> 9 * John Morris <john AT zultron DOT com> 10 * Michael Haberler <license AT mah DOT priv DOT at> 11 * Copyright (C) 2014 Jeff Epler <jepler@unpythonic.net> 12 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Lesser General Public 15 * License as published by the Free Software Foundation; either 16 * version 2.1 of the License, or (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Lesser General Public License for more details. 22 * 23 * You should have received a copy of the GNU Lesser General Public 24 * License along with this library; if not, write to the Free Software 25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26 ********************************************************************/ 27 28 #ifndef _GNU_SOURCE 29 #define _GNU_SOURCE 30 #endif 31 32 #include "config.h" 33 34 #include <rtapi.h> 35 #include <rtapi_pci.h> 36 #include <rtapi_firmware.h> 37 #include "rtapi_uspace.hh" 38 39 #include <dirent.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <map> 43 #include <stddef.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #ifdef HAVE_SYS_IO_H 48 #include <sys/io.h> 49 #endif 50 #include <sys/mman.h> 51 #include <sys/utsname.h> 52 #include <time.h> 53 #include <unistd.h> 54 #include <vector> 55 #include <sys/types.h> 56 #include <sys/stat.h> 57 58 #ifdef NO_LIBUDEV 59 int rtapi_pci_register_driver(struct rtapi_pci_driver *driver) { 60 return -ENODEV; 61 } 62 void rtapi_pci_unregister_driver(struct rtapi_pci_driver *driver) 63 { 64 } 65 66 void rtapi__iomem *rtapi_pci_ioremap_bar(struct rtapi_pci_dev *dev, int bar) 67 { 68 return NULL; 69 } 70 71 void rtapi_iounmap(volatile void rtapi__iomem *addr) 72 { 73 } 74 75 int rtapi_pci_enable_device(struct rtapi_pci_dev *dev) 76 { 77 return -ENODEV; 78 } 79 80 int rtapi_pci_disble_device(struct rtapi_pci_dev *dev) 81 { 82 return -ENODEV; 83 } 84 85 #else 86 #include <libudev.h> 87 typedef std::vector<rtapi_pci_dev*> DeviceVector; 88 89 DeviceVector &DEVICES(rtapi_pci_driver *driver) { 90 return *reinterpret_cast<DeviceVector*>(driver->private_data); 91 } 92 93 static int check_device_id(struct udev_device *udev_dev, struct rtapi_pci_device_id *dev_id, struct rtapi_pci_driver *driver) 94 { 95 ssize_t res; 96 const char *pval; 97 unsigned int i; 98 99 /* Read the ID values into the dev_id structure */ 100 101 pval = udev_device_get_property_value (udev_dev, "PCI_ID"); 102 res = sscanf(pval, "%X:%X", &dev_id->vendor, &dev_id->device); 103 if (res != 2) { 104 rtapi_print_msg(RTAPI_MSG_ERR, 105 "Unable to parse vendor:device from '%s'\n", pval); 106 return -1; 107 } 108 109 pval = udev_device_get_property_value (udev_dev, "PCI_SUBSYS_ID"); 110 res = sscanf(pval, "%X:%X", &dev_id->subvendor, &dev_id->subdevice); 111 if (res != 2) { 112 rtapi_print_msg(RTAPI_MSG_ERR, 113 "Unable to parse subvendor:subdevice from '%s'\n", pval); 114 return -1; 115 } 116 117 rtapi_print_msg(RTAPI_MSG_DBG,"PCI_ID: %04X:%04X %04X:%04X\n", 118 dev_id->vendor, dev_id->device, 119 dev_id->subvendor, dev_id->subdevice); 120 121 /* Compare values with list of valid IDs from driver struct */ 122 for (i=0; driver->id_table[i].vendor != 0; i++) { 123 if (dev_id->vendor != driver->id_table[i].vendor) 124 continue; 125 if (dev_id->device != driver->id_table[i].device) 126 continue; 127 if (dev_id->subvendor != driver->id_table[i].subvendor) 128 continue; 129 if (dev_id->subdevice != driver->id_table[i].subdevice) 130 continue; 131 132 /* Everything matched up! */ 133 return 0; 134 } 135 136 /* ID was not present in driver ID table */ 137 return -1; 138 } 139 140 int rtapi_pci_register_driver(struct rtapi_pci_driver *driver) 141 { 142 driver->private_data = reinterpret_cast<void*>(new DeviceVector); 143 WITH_ROOT; 144 struct udev *udev; 145 struct udev_enumerate *enumerate; 146 struct udev_list_entry *devices, *dev_list_entry; 147 struct udev_device *udev_dev; 148 char buf[256]; 149 150 ssize_t res; 151 struct rtapi_pci_dev *dev = NULL; 152 struct rtapi_pci_device_id dev_id; 153 154 /* Create the udev object */ 155 udev = udev_new(); 156 if (!udev) { 157 rtapi_print_msg(RTAPI_MSG_ERR,"Can't create udev\n"); 158 return -1; 159 } 160 161 /* Create a filter that matches the PCI_IDs we're looking for */ 162 enumerate = udev_enumerate_new(udev); 163 int err = udev_enumerate_add_match_subsystem(enumerate, "pci"); 164 if (err != 0) { 165 rtapi_print_msg(RTAPI_MSG_ERR, 166 "Failed to register subsystem match: pci\n"); 167 goto error; 168 } 169 170 for (int i=0; driver->id_table[i].vendor; i++) { 171 snprintf(buf, sizeof(buf), "%04X:%04X", 172 driver->id_table[i].vendor, 173 driver->id_table[i].device); 174 175 err = udev_enumerate_add_match_property(enumerate, "PCI_ID", buf); 176 if (err) { 177 rtapi_print_msg(RTAPI_MSG_ERR, 178 "Failed to register property match: PCI_ID=%s\n", 179 buf); 180 goto error; 181 } 182 } 183 184 udev_enumerate_scan_devices(enumerate); 185 devices = udev_enumerate_get_list_entry(enumerate); 186 187 udev_list_entry_foreach(dev_list_entry, devices) { 188 const char *name; 189 190 /* Get the filename of the /sys entry for the device 191 and create a udev_device object (dev) representing it */ 192 name = udev_list_entry_get_name(dev_list_entry); 193 udev_dev = udev_device_new_from_syspath(udev, name); 194 195 /* We have a device...see if it matches the ID list in the driver */ 196 int r = check_device_id(udev_dev, &dev_id, driver); 197 if (r < 0) { 198 udev_device_unref(udev_dev); 199 continue; 200 } 201 202 /* We got a device! */ 203 204 /* Allocate and clear a device structure */ 205 dev = new rtapi_pci_dev; 206 if (!dev) { 207 rtapi_print_msg(RTAPI_MSG_ERR, "Out of memory\n"); 208 udev_device_unref(udev_dev); 209 goto error; 210 } 211 212 213 /* Initialize device structure */ 214 strncpy(dev->dev_name, 215 udev_device_get_property_value (udev_dev, "PCI_SLOT_NAME"), 216 sizeof(dev->dev_name) - 1); 217 218 strncpy(dev->sys_path, name, 219 sizeof(dev->sys_path) - 1); 220 221 dev->vendor = dev_id.vendor; 222 dev->device = dev_id.device; 223 dev->subsystem_vendor = dev_id.subvendor; 224 dev->subsystem_device = dev_id.subdevice; 225 dev->driver_data = NULL; 226 227 udev_device_unref(udev_dev); 228 229 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: DeviceID: %04X %04X %04X %04X\n", 230 dev->vendor, 231 dev->device, 232 dev->subsystem_vendor, 233 dev->subsystem_device ); 234 235 /* Call module init code */ 236 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Calling driver probe function\n"); 237 res = driver->probe(dev, &dev_id); 238 if (res != 0) { 239 delete dev; 240 rtapi_print_msg(RTAPI_MSG_ERR, "Driver probe function failed!\n"); 241 rtapi_pci_unregister_driver(driver); 242 goto error; 243 } 244 DEVICES(driver).push_back(dev); 245 } 246 /* Free the enumerator object */ 247 udev_enumerate_unref(enumerate); 248 249 udev_unref(udev); 250 return 0; 251 252 error: 253 udev_enumerate_unref(enumerate); 254 udev_unref(udev); 255 return -1; 256 } 257 258 void rtapi_pci_unregister_driver(struct rtapi_pci_driver *driver) 259 { 260 if (!driver) return; 261 262 WITH_ROOT; 263 for(auto dev : DEVICES(driver)) 264 { 265 driver->remove(dev); 266 delete dev; 267 } 268 delete &DEVICES(driver); 269 driver->private_data = 0; 270 } 271 272 typedef std::map<void rtapi__iomem*, size_t> IoMap; 273 IoMap iomaps; 274 275 void rtapi__iomem *rtapi_pci_ioremap_bar(struct rtapi_pci_dev *dev, int bar) 276 { 277 WITH_ROOT; 278 void *mmio; 279 char path[256]; 280 281 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Map BAR %i\n", bar); 282 283 if (bar < 0 || bar >= 6) { 284 rtapi_print_msg(RTAPI_MSG_ERR, "Invalid PCI BAR %d\n", bar); 285 return NULL; 286 } 287 288 snprintf(path, sizeof(path), "%s/resource%i", dev->sys_path, bar); 289 290 /* Open the resource node */ 291 int fd = open(path, O_RDWR | O_SYNC); 292 if (fd < 0) { 293 rtapi_print_msg(RTAPI_MSG_ERR, "Could not open resource \"%s\". (%s)\n", 294 path, strerror(errno)); 295 return NULL; 296 } 297 298 size_t resource_len = rtapi_pci_resource_len(dev, bar); 299 mmio = mmap(NULL, resource_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 300 close(fd); 301 302 if (mmio == NULL || mmio == MAP_FAILED) { 303 rtapi_print_msg(RTAPI_MSG_ERR, "Failed to remap MMIO %d of PCI device %s: %s\n", 304 bar, dev->dev_name, strerror(errno)); 305 return NULL; 306 } 307 308 iomaps[mmio] = resource_len; 309 310 return mmio; 311 } 312 313 void rtapi_iounmap(volatile void rtapi__iomem *addr) 314 { 315 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Unmap BAR\n"); 316 317 if (!addr) return; 318 319 IoMap::iterator it = iomaps.find(const_cast<void*>(addr)); 320 if(it == iomaps.end()) { 321 rtapi_print_msg(RTAPI_MSG_ERR, "IO-unmap: Did not find PCI mapping %p", addr); 322 return; 323 } 324 325 munmap(it->first, it->second); 326 rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI_PCI: Unmapped %zd bytes at %p\n", it->second, addr); 327 iomaps.erase(it); 328 } 329 330 char *get_sys_enable_path(const rtapi_pci_dev *dev, char *path, size_t npath) { 331 snprintf(path, npath, "%s/enable", dev->sys_path); 332 // kernel 3.12 renamed this file to "enabled"... doh 333 if(access(path, F_OK) < 0) 334 snprintf(path, npath, "%s/enabled", dev->sys_path); 335 return path; 336 } 337 338 int rtapi_pci_enable_device(struct rtapi_pci_dev *dev) 339 { 340 WITH_ROOT; 341 FILE *stream; 342 char path[256]; 343 int i,r; 344 unsigned long long L1, L2, L3; 345 346 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Enabling Device %s\n", dev->dev_name); 347 348 /* Enable the device */ 349 stream = fopen(get_sys_enable_path(dev, path, sizeof(path)), "w"); 350 if (stream == NULL) { 351 rtapi_print_msg(RTAPI_MSG_ERR, 352 "Failed to open \"%s\" (%s)\n", 353 path, strerror(errno)); 354 return -1; 355 } 356 fprintf(stream, "1"); 357 fclose(stream); 358 359 /* Open the resource file... */ 360 snprintf(path, sizeof(path), "%s/resource", dev->sys_path); 361 stream = fopen(path, "r"); 362 if (stream == NULL) { 363 rtapi_print_msg(RTAPI_MSG_ERR, 364 "Failed to open \"%s\" (%s)\n", 365 path, strerror(errno)); 366 return -1; 367 } 368 369 /* ...and read in the data */ 370 for (i=0; i < 6; i++) { 371 r=fscanf(stream, "%Lx %Lx %Lx", &L1, &L2, &L3); 372 if (r != 3) { 373 rtapi_print_msg(RTAPI_MSG_ERR,"Failed to parse \"%s\"\n", path); 374 fclose(stream); 375 return -1; 376 } 377 dev->resource[i].start = L1; 378 dev->resource[i].end = L2; 379 dev->resource[i].flags = (unsigned long) L3; 380 381 rtapi_print_msg(RTAPI_MSG_DBG,"Resource %d: %p %p %08lx\n", i, 382 (void*)dev->resource[i].start, 383 (void*)dev->resource[i].end, 384 dev->resource[i].flags); 385 } 386 387 fclose(stream); 388 return 0; 389 } 390 391 int rtapi_pci_disable_device(struct rtapi_pci_dev *dev) 392 { 393 WITH_ROOT; 394 FILE *stream; 395 char path[256]; 396 int r; 397 398 rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Disable Device\n"); 399 400 /* Enable the device */ 401 stream = fopen(get_sys_enable_path(dev, path, sizeof(path)), "w"); 402 if (stream == NULL) { 403 rtapi_print_msg(RTAPI_MSG_ERR, 404 "Failed to open \"%s\" (%s)\n", 405 path, strerror(errno)); 406 return -1; 407 } 408 r = fprintf(stream, "0"); 409 if (r != 1) 410 rtapi_print_msg(RTAPI_MSG_ERR, 411 "Failed to disable device \"%s\" (%s)\n", 412 dev->dev_name, strerror(errno)); 413 else 414 r = 0; 415 416 fclose(stream); 417 418 return r; 419 } 420 #endif 421 422 int rtapi_request_firmware(const struct rtapi_firmware **fw, const char *name, struct rtapi_device *device) { 423 struct rtapi_firmware *lfw; 424 struct utsname sysinfo; 425 const char *basepath = "/lib/firmware"; 426 char path[256]; 427 struct stat st; 428 int r; 429 int fd = -1; 430 431 /* Allocate and initialize a firmware struct */ 432 lfw = new rtapi_firmware; 433 if (lfw == NULL) { 434 rtapi_print_msg(RTAPI_MSG_ERR, "Out of memory\n"); 435 return -ENOMEM; 436 } 437 438 /* Try to open the kernel-specific file */ 439 r = uname(&sysinfo); 440 if (r >= 0) { 441 snprintf(path, sizeof(path), "/%s/%s/%s", basepath, sysinfo.release, name); 442 fd = open(path, O_RDONLY); 443 } 444 445 /* If we don't have a valid file descriptor yet, try an alternate location */ 446 if (fd < 0) { 447 snprintf(path, sizeof(path), "/%s/%s", basepath, name); 448 fd = open(path, O_RDONLY); 449 } 450 451 452 /* If we don't have a valid file descriptor by here, it's an error */ 453 if (fd < 0) { 454 rtapi_print_msg(RTAPI_MSG_ERR, "Could not locate firmware \"%s\". (%s)\n", 455 path, strerror(errno)); 456 delete lfw; 457 return -ENOENT; 458 } 459 460 /* We've found and oepned the file, now let's get the size */ 461 if (stat(path, &st) < 0) 462 { 463 rtapi_print_msg(RTAPI_MSG_ERR, "Could not determine size of file \"%s\". (%s)\n", 464 path, strerror(errno)); 465 delete lfw; 466 return -1; 467 } 468 469 lfw->size = st.st_size; 470 lfw->data = reinterpret_cast<const rtapi_u8*>(mmap(NULL, lfw->size, PROT_READ, MAP_PRIVATE, fd, 0)); 471 472 if (lfw->data == NULL || lfw->data == MAP_FAILED) { 473 if (lfw->data == NULL) 474 munmap((void*)lfw->data, lfw->size); 475 rtapi_print_msg(RTAPI_MSG_ERR, "Failed to mmap file %s\n", path); 476 delete lfw; 477 return -1; 478 } 479 480 *fw = lfw; 481 return 0; 482 } 483 484 void rtapi_release_firmware(const struct rtapi_firmware *fw) { 485 munmap((void*)fw->data, fw->size); 486 delete fw; 487 } 488 489 EXPORT_SYMBOL(rtapi_iounmap); 490 EXPORT_SYMBOL(rtapi_pci_enable_device); 491 EXPORT_SYMBOL(rtapi_pci_disable_device); 492 EXPORT_SYMBOL(rtapi_pci_register_driver); 493 EXPORT_SYMBOL(rtapi_pci_unregister_driver); 494 EXPORT_SYMBOL(rtapi_pci_ioremap_bar);