/ src / rtapi / rtapi_pci.cc
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);