virtio: move left PCI stuff in the right file
[dpdk.git] / drivers / net / virtio / virtio_pci.c
index 8d001e8..e89a044 100644 (file)
  */
 #include <stdint.h>
 
+#ifdef RTE_EXEC_ENV_LINUXAPP
+ #include <dirent.h>
+ #include <fcntl.h>
+#endif
+
 #include "virtio_pci.h"
 #include "virtio_logs.h"
 #include "virtqueue.h"
@@ -156,6 +161,266 @@ legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
        VIRTIO_WRITE_REG_2(hw, VIRTIO_PCI_QUEUE_NOTIFY, vq->vq_queue_index);
 }
 
+#ifdef RTE_EXEC_ENV_LINUXAPP
+static int
+parse_sysfs_value(const char *filename, unsigned long *val)
+{
+       FILE *f;
+       char buf[BUFSIZ];
+       char *end = NULL;
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               PMD_INIT_LOG(ERR, "%s(): cannot open sysfs value %s",
+                            __func__, filename);
+               return -1;
+       }
+
+       if (fgets(buf, sizeof(buf), f) == NULL) {
+               PMD_INIT_LOG(ERR, "%s(): cannot read sysfs value %s",
+                            __func__, filename);
+               fclose(f);
+               return -1;
+       }
+       *val = strtoul(buf, &end, 0);
+       if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) {
+               PMD_INIT_LOG(ERR, "%s(): cannot parse sysfs value %s",
+                            __func__, filename);
+               fclose(f);
+               return -1;
+       }
+       fclose(f);
+       return 0;
+}
+
+static int
+get_uio_dev(struct rte_pci_addr *loc, char *buf, unsigned int buflen,
+                       unsigned int *uio_num)
+{
+       struct dirent *e;
+       DIR *dir;
+       char dirname[PATH_MAX];
+
+       /*
+        * depending on kernel version, uio can be located in uio/uioX
+        * or uio:uioX
+        */
+       snprintf(dirname, sizeof(dirname),
+                    SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio",
+                    loc->domain, loc->bus, loc->devid, loc->function);
+       dir = opendir(dirname);
+       if (dir == NULL) {
+               /* retry with the parent directory */
+               snprintf(dirname, sizeof(dirname),
+                            SYSFS_PCI_DEVICES "/" PCI_PRI_FMT,
+                            loc->domain, loc->bus, loc->devid, loc->function);
+               dir = opendir(dirname);
+
+               if (dir == NULL) {
+                       PMD_INIT_LOG(ERR, "Cannot opendir %s", dirname);
+                       return -1;
+               }
+       }
+
+       /* take the first file starting with "uio" */
+       while ((e = readdir(dir)) != NULL) {
+               /* format could be uio%d ...*/
+               int shortprefix_len = sizeof("uio") - 1;
+               /* ... or uio:uio%d */
+               int longprefix_len = sizeof("uio:uio") - 1;
+               char *endptr;
+
+               if (strncmp(e->d_name, "uio", 3) != 0)
+                       continue;
+
+               /* first try uio%d */
+               errno = 0;
+               *uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10);
+               if (errno == 0 && endptr != (e->d_name + shortprefix_len)) {
+                       snprintf(buf, buflen, "%s/uio%u", dirname, *uio_num);
+                       break;
+               }
+
+               /* then try uio:uio%d */
+               errno = 0;
+               *uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10);
+               if (errno == 0 && endptr != (e->d_name + longprefix_len)) {
+                       snprintf(buf, buflen, "%s/uio:uio%u", dirname,
+                                    *uio_num);
+                       break;
+               }
+       }
+       closedir(dir);
+
+       /* No uio resource found */
+       if (e == NULL) {
+               PMD_INIT_LOG(ERR, "Could not find uio resource");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+legacy_virtio_has_msix(const struct rte_pci_addr *loc)
+{
+       DIR *d;
+       char dirname[PATH_MAX];
+
+       snprintf(dirname, sizeof(dirname),
+                    SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/msi_irqs",
+                    loc->domain, loc->bus, loc->devid, loc->function);
+
+       d = opendir(dirname);
+       if (d)
+               closedir(d);
+
+       return (d != NULL);
+}
+
+/* Extract I/O port numbers from sysfs */
+static int
+virtio_resource_init_by_uio(struct rte_pci_device *pci_dev)
+{
+       char dirname[PATH_MAX];
+       char filename[PATH_MAX];
+       unsigned long start, size;
+       unsigned int uio_num;
+
+       if (get_uio_dev(&pci_dev->addr, dirname, sizeof(dirname), &uio_num) < 0)
+               return -1;
+
+       /* get portio size */
+       snprintf(filename, sizeof(filename),
+                    "%s/portio/port0/size", dirname);
+       if (parse_sysfs_value(filename, &size) < 0) {
+               PMD_INIT_LOG(ERR, "%s(): cannot parse size",
+                            __func__);
+               return -1;
+       }
+
+       /* get portio start */
+       snprintf(filename, sizeof(filename),
+                "%s/portio/port0/start", dirname);
+       if (parse_sysfs_value(filename, &start) < 0) {
+               PMD_INIT_LOG(ERR, "%s(): cannot parse portio start",
+                            __func__);
+               return -1;
+       }
+       pci_dev->mem_resource[0].addr = (void *)(uintptr_t)start;
+       pci_dev->mem_resource[0].len =  (uint64_t)size;
+       PMD_INIT_LOG(DEBUG,
+                    "PCI Port IO found start=0x%lx with size=0x%lx",
+                    start, size);
+
+       /* save fd */
+       memset(dirname, 0, sizeof(dirname));
+       snprintf(dirname, sizeof(dirname), "/dev/uio%u", uio_num);
+       pci_dev->intr_handle.fd = open(dirname, O_RDWR);
+       if (pci_dev->intr_handle.fd < 0) {
+               PMD_INIT_LOG(ERR, "Cannot open %s: %s\n",
+                       dirname, strerror(errno));
+               return -1;
+       }
+
+       pci_dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
+       pci_dev->driver->drv_flags |= RTE_PCI_DRV_INTR_LSC;
+
+       return 0;
+}
+
+/* Extract port I/O numbers from proc/ioports */
+static int
+virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev)
+{
+       uint16_t start, end;
+       int size;
+       FILE *fp;
+       char *line = NULL;
+       char pci_id[16];
+       int found = 0;
+       size_t linesz;
+
+       snprintf(pci_id, sizeof(pci_id), PCI_PRI_FMT,
+                pci_dev->addr.domain,
+                pci_dev->addr.bus,
+                pci_dev->addr.devid,
+                pci_dev->addr.function);
+
+       fp = fopen("/proc/ioports", "r");
+       if (fp == NULL) {
+               PMD_INIT_LOG(ERR, "%s(): can't open ioports", __func__);
+               return -1;
+       }
+
+       while (getdelim(&line, &linesz, '\n', fp) > 0) {
+               char *ptr = line;
+               char *left;
+               int n;
+
+               n = strcspn(ptr, ":");
+               ptr[n] = 0;
+               left = &ptr[n + 1];
+
+               while (*left && isspace(*left))
+                       left++;
+
+               if (!strncmp(left, pci_id, strlen(pci_id))) {
+                       found = 1;
+
+                       while (*ptr && isspace(*ptr))
+                               ptr++;
+
+                       sscanf(ptr, "%04hx-%04hx", &start, &end);
+                       size = end - start + 1;
+
+                       break;
+               }
+       }
+
+       free(line);
+       fclose(fp);
+
+       if (!found)
+               return -1;
+
+       pci_dev->mem_resource[0].addr = (void *)(uintptr_t)(uint32_t)start;
+       pci_dev->mem_resource[0].len =  (uint64_t)size;
+       PMD_INIT_LOG(DEBUG,
+               "PCI Port IO found start=0x%x with size=0x%x",
+               start, size);
+
+       /* can't support lsc interrupt without uio */
+       pci_dev->driver->drv_flags &= ~RTE_PCI_DRV_INTR_LSC;
+
+       return 0;
+}
+
+/* Extract I/O port numbers from sysfs */
+static int
+legacy_virtio_resource_init(struct rte_pci_device *pci_dev)
+{
+       if (virtio_resource_init_by_uio(pci_dev) == 0)
+               return 0;
+       else
+               return virtio_resource_init_by_ioports(pci_dev);
+}
+
+#else
+static int
+legayc_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused)
+{
+       /* nic_uio does not enable interrupts, return 0 (false). */
+       return 0;
+}
+
+static int
+legacy_virtio_resource_init(struct rte_pci_device *pci_dev __rte_unused)
+{
+       /* no setup required */
+       return 0;
+}
+#endif
 
 static const struct virtio_pci_ops legacy_ops = {
        .read_dev_cfg   = legacy_read_dev_config,
@@ -241,9 +506,14 @@ vtpci_irq_config(struct virtio_hw *hw, uint16_t vec)
 }
 
 int
-vtpci_init(struct rte_pci_device *dev __rte_unused, struct virtio_hw *hw)
+vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw)
 {
        hw->vtpci_ops = &legacy_ops;
 
+       if (legacy_virtio_resource_init(dev) < 0)
+               return -1;
+       hw->use_msix = legacy_virtio_has_msix(&dev->addr);
+       hw->io_base  = (uint32_t)(uintptr_t)dev->mem_resource[0].addr;
+
        return 0;
 }