#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "config.h"
#include <rtapi.h>
#include <rtapi_pci.h>
#include <rtapi_firmware.h>
#include "rtapi_uspace.hh"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <map>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_IO_H
#include <sys/io.h>
#endif
#include <sys/mman.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef NO_LIBUDEV
int rtapi_pci_register_driver(struct rtapi_pci_driver *driver) {
return -ENODEV;
}
void rtapi_pci_unregister_driver(struct rtapi_pci_driver *driver)
{
}
void rtapi__iomem *rtapi_pci_ioremap_bar(struct rtapi_pci_dev *dev, int bar)
{
return NULL;
}
void rtapi_iounmap(volatile void rtapi__iomem *addr)
{
}
int rtapi_pci_enable_device(struct rtapi_pci_dev *dev)
{
return -ENODEV;
}
int rtapi_pci_disble_device(struct rtapi_pci_dev *dev)
{
return -ENODEV;
}
#else
#include <libudev.h>
typedef std::vector<rtapi_pci_dev*> DeviceVector;
DeviceVector &DEVICES(rtapi_pci_driver *driver) {
return *reinterpret_cast<DeviceVector*>(driver->private_data);
}
static int check_device_id(struct udev_device *udev_dev, struct rtapi_pci_device_id *dev_id, struct rtapi_pci_driver *driver)
{
ssize_t res;
const char *pval;
unsigned int i;
pval = udev_device_get_property_value (udev_dev, "PCI_ID");
res = sscanf(pval, "%X:%X", &dev_id->vendor, &dev_id->device);
if (res != 2) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Unable to parse vendor:device from '%s'\n", pval);
return -1;
}
pval = udev_device_get_property_value (udev_dev, "PCI_SUBSYS_ID");
res = sscanf(pval, "%X:%X", &dev_id->subvendor, &dev_id->subdevice);
if (res != 2) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Unable to parse subvendor:subdevice from '%s'\n", pval);
return -1;
}
rtapi_print_msg(RTAPI_MSG_DBG,"PCI_ID: %04X:%04X %04X:%04X\n",
dev_id->vendor, dev_id->device,
dev_id->subvendor, dev_id->subdevice);
for (i=0; driver->id_table[i].vendor != 0; i++) {
if (dev_id->vendor != driver->id_table[i].vendor)
continue;
if (dev_id->device != driver->id_table[i].device)
continue;
if (dev_id->subvendor != driver->id_table[i].subvendor)
continue;
if (dev_id->subdevice != driver->id_table[i].subdevice)
continue;
return 0;
}
return -1;
}
int rtapi_pci_register_driver(struct rtapi_pci_driver *driver)
{
driver->private_data = reinterpret_cast<void*>(new DeviceVector);
WITH_ROOT;
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *udev_dev;
char buf[256];
ssize_t res;
struct rtapi_pci_dev *dev = NULL;
struct rtapi_pci_device_id dev_id;
udev = udev_new();
if (!udev) {
rtapi_print_msg(RTAPI_MSG_ERR,"Can't create udev\n");
return -1;
}
enumerate = udev_enumerate_new(udev);
int err = udev_enumerate_add_match_subsystem(enumerate, "pci");
if (err != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to register subsystem match: pci\n");
goto error;
}
for (int i=0; driver->id_table[i].vendor; i++) {
snprintf(buf, sizeof(buf), "%04X:%04X",
driver->id_table[i].vendor,
driver->id_table[i].device);
err = udev_enumerate_add_match_property(enumerate, "PCI_ID", buf);
if (err) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to register property match: PCI_ID=%s\n",
buf);
goto error;
}
}
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
const char *name;
name = udev_list_entry_get_name(dev_list_entry);
udev_dev = udev_device_new_from_syspath(udev, name);
int r = check_device_id(udev_dev, &dev_id, driver);
if (r < 0) {
udev_device_unref(udev_dev);
continue;
}
dev = new rtapi_pci_dev;
if (!dev) {
rtapi_print_msg(RTAPI_MSG_ERR, "Out of memory\n");
udev_device_unref(udev_dev);
goto error;
}
strncpy(dev->dev_name,
udev_device_get_property_value (udev_dev, "PCI_SLOT_NAME"),
sizeof(dev->dev_name) - 1);
strncpy(dev->sys_path, name,
sizeof(dev->sys_path) - 1);
dev->vendor = dev_id.vendor;
dev->device = dev_id.device;
dev->subsystem_vendor = dev_id.subvendor;
dev->subsystem_device = dev_id.subdevice;
dev->driver_data = NULL;
udev_device_unref(udev_dev);
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: DeviceID: %04X %04X %04X %04X\n",
dev->vendor,
dev->device,
dev->subsystem_vendor,
dev->subsystem_device );
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Calling driver probe function\n");
res = driver->probe(dev, &dev_id);
if (res != 0) {
delete dev;
rtapi_print_msg(RTAPI_MSG_ERR, "Driver probe function failed!\n");
rtapi_pci_unregister_driver(driver);
goto error;
}
DEVICES(driver).push_back(dev);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return 0;
error:
udev_enumerate_unref(enumerate);
udev_unref(udev);
return -1;
}
void rtapi_pci_unregister_driver(struct rtapi_pci_driver *driver)
{
if (!driver) return;
WITH_ROOT;
for(auto dev : DEVICES(driver))
{
driver->remove(dev);
delete dev;
}
delete &DEVICES(driver);
driver->private_data = 0;
}
typedef std::map<void rtapi__iomem*, size_t> IoMap;
IoMap iomaps;
void rtapi__iomem *rtapi_pci_ioremap_bar(struct rtapi_pci_dev *dev, int bar)
{
WITH_ROOT;
void *mmio;
char path[256];
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Map BAR %i\n", bar);
if (bar < 0 || bar >= 6) {
rtapi_print_msg(RTAPI_MSG_ERR, "Invalid PCI BAR %d\n", bar);
return NULL;
}
snprintf(path, sizeof(path), "%s/resource%i", dev->sys_path, bar);
int fd = open(path, O_RDWR | O_SYNC);
if (fd < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "Could not open resource \"%s\". (%s)\n",
path, strerror(errno));
return NULL;
}
size_t resource_len = rtapi_pci_resource_len(dev, bar);
mmio = mmap(NULL, resource_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if (mmio == NULL || mmio == MAP_FAILED) {
rtapi_print_msg(RTAPI_MSG_ERR, "Failed to remap MMIO %d of PCI device %s: %s\n",
bar, dev->dev_name, strerror(errno));
return NULL;
}
iomaps[mmio] = resource_len;
return mmio;
}
void rtapi_iounmap(volatile void rtapi__iomem *addr)
{
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Unmap BAR\n");
if (!addr) return;
IoMap::iterator it = iomaps.find(const_cast<void*>(addr));
if(it == iomaps.end()) {
rtapi_print_msg(RTAPI_MSG_ERR, "IO-unmap: Did not find PCI mapping %p", addr);
return;
}
munmap(it->first, it->second);
rtapi_print_msg(RTAPI_MSG_ERR, "RTAPI_PCI: Unmapped %zd bytes at %p\n", it->second, addr);
iomaps.erase(it);
}
char *get_sys_enable_path(const rtapi_pci_dev *dev, char *path, size_t npath) {
snprintf(path, npath, "%s/enable", dev->sys_path);
if(access(path, F_OK) < 0)
snprintf(path, npath, "%s/enabled", dev->sys_path);
return path;
}
int rtapi_pci_enable_device(struct rtapi_pci_dev *dev)
{
WITH_ROOT;
FILE *stream;
char path[256];
int i,r;
unsigned long long L1, L2, L3;
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Enabling Device %s\n", dev->dev_name);
stream = fopen(get_sys_enable_path(dev, path, sizeof(path)), "w");
if (stream == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to open \"%s\" (%s)\n",
path, strerror(errno));
return -1;
}
fprintf(stream, "1");
fclose(stream);
snprintf(path, sizeof(path), "%s/resource", dev->sys_path);
stream = fopen(path, "r");
if (stream == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to open \"%s\" (%s)\n",
path, strerror(errno));
return -1;
}
for (i=0; i < 6; i++) {
r=fscanf(stream, "%Lx %Lx %Lx", &L1, &L2, &L3);
if (r != 3) {
rtapi_print_msg(RTAPI_MSG_ERR,"Failed to parse \"%s\"\n", path);
fclose(stream);
return -1;
}
dev->resource[i].start = L1;
dev->resource[i].end = L2;
dev->resource[i].flags = (unsigned long) L3;
rtapi_print_msg(RTAPI_MSG_DBG,"Resource %d: %p %p %08lx\n", i,
(void*)dev->resource[i].start,
(void*)dev->resource[i].end,
dev->resource[i].flags);
}
fclose(stream);
return 0;
}
int rtapi_pci_disable_device(struct rtapi_pci_dev *dev)
{
WITH_ROOT;
FILE *stream;
char path[256];
int r;
rtapi_print_msg(RTAPI_MSG_DBG, "RTAPI_PCI: Disable Device\n");
stream = fopen(get_sys_enable_path(dev, path, sizeof(path)), "w");
if (stream == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to open \"%s\" (%s)\n",
path, strerror(errno));
return -1;
}
r = fprintf(stream, "0");
if (r != 1)
rtapi_print_msg(RTAPI_MSG_ERR,
"Failed to disable device \"%s\" (%s)\n",
dev->dev_name, strerror(errno));
else
r = 0;
fclose(stream);
return r;
}
#endif
int rtapi_request_firmware(const struct rtapi_firmware **fw, const char *name, struct rtapi_device *device) {
struct rtapi_firmware *lfw;
struct utsname sysinfo;
const char *basepath = "/lib/firmware";
char path[256];
struct stat st;
int r;
int fd = -1;
lfw = new rtapi_firmware;
if (lfw == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, "Out of memory\n");
return -ENOMEM;
}
r = uname(&sysinfo);
if (r >= 0) {
snprintf(path, sizeof(path), "/%s/%s/%s", basepath, sysinfo.release, name);
fd = open(path, O_RDONLY);
}
if (fd < 0) {
snprintf(path, sizeof(path), "/%s/%s", basepath, name);
fd = open(path, O_RDONLY);
}
if (fd < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "Could not locate firmware \"%s\". (%s)\n",
path, strerror(errno));
delete lfw;
return -ENOENT;
}
if (stat(path, &st) < 0)
{
rtapi_print_msg(RTAPI_MSG_ERR, "Could not determine size of file \"%s\". (%s)\n",
path, strerror(errno));
delete lfw;
return -1;
}
lfw->size = st.st_size;
lfw->data = reinterpret_cast<const rtapi_u8*>(mmap(NULL, lfw->size, PROT_READ, MAP_PRIVATE, fd, 0));
if (lfw->data == NULL || lfw->data == MAP_FAILED) {
if (lfw->data == NULL)
munmap((void*)lfw->data, lfw->size);
rtapi_print_msg(RTAPI_MSG_ERR, "Failed to mmap file %s\n", path);
delete lfw;
return -1;
}
*fw = lfw;
return 0;
}
void rtapi_release_firmware(const struct rtapi_firmware *fw) {
munmap((void*)fw->data, fw->size);
delete fw;
}
EXPORT_SYMBOL(rtapi_iounmap);
EXPORT_SYMBOL(rtapi_pci_enable_device);
EXPORT_SYMBOL(rtapi_pci_disable_device);
EXPORT_SYMBOL(rtapi_pci_register_driver);
EXPORT_SYMBOL(rtapi_pci_unregister_driver);
EXPORT_SYMBOL(rtapi_pci_ioremap_bar);