diff options
Diffstat (limited to 'pci-arbiter/func_files.c')
-rw-r--r-- | pci-arbiter/func_files.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/pci-arbiter/func_files.c b/pci-arbiter/func_files.c new file mode 100644 index 00000000..7df94d2f --- /dev/null +++ b/pci-arbiter/func_files.c @@ -0,0 +1,210 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Per-function files implementation. + * + * Implementation of all files repeated for each function. + */ + +#include "func_files.h" + +#include <assert.h> +#include <sys/io.h> + +/* Read or write a block of data from/to the configuration space */ +static error_t +config_block_op (struct pci_device *dev, off_t offset, size_t * len, + void *data, pci_io_op_t op) +{ + error_t err; + size_t pendent = *len; + + while (pendent >= 4) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 4); + if (err) + return err; + + offset += 4; + data += 4; + pendent -= 4; + } + + if (pendent >= 2) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 2); + if (err) + return err; + + offset += 2; + data += 2; + pendent -= 2; + } + + if (pendent) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 1); + if (err) + return err; + + offset++; + data++; + pendent--; + } + + *len -= pendent; + + return 0; +} + +/* Read or write from/to the config file */ +error_t +io_config_file (struct pci_device * dev, off_t offset, size_t * len, + void *data, pci_io_op_t op) +{ + error_t err; + + /* This should never happen */ + assert_backtrace (dev != 0); + + /* Don't exceed the config space size */ + if (offset > dev->config_size) + return EINVAL; + if ((offset + *len) > dev->config_size) + *len = dev->config_size - offset; + + pthread_mutex_lock (&fs->pci_conf_lock); + err = config_block_op (dev, offset, len, data, op); + pthread_mutex_unlock (&fs->pci_conf_lock); + + return err; +} + +/* Read the mapped ROM */ +error_t +read_rom_file (struct pci_device * dev, off_t offset, size_t * len, + void *data) +{ + error_t err; + + /* This should never happen */ + assert_backtrace (dev != 0); + + /* Refresh the ROM */ + err = pci_sys->device_refresh (dev, -1, 1); + if (err) + return err; + + /* Don't exceed the ROM size */ + if (offset > dev->rom_size) + return EINVAL; + if ((offset + *len) > dev->rom_size) + *len = dev->rom_size - offset; + + memcpy (data, dev->rom_memory + offset, *len); + + return 0; +} + +/* Read or write from/to a memory region by using I/O ports */ +static error_t +region_block_ioport_op (uint16_t port, off_t offset, size_t * len, + void *data, int read) +{ + size_t pending = *len; + + while (pending >= 4) + { + /* read == true: read; else: write */ + if (read) + *((unsigned int *) data) = inl (port + offset); + else + outl (*((unsigned int *) data), port + offset); + + offset += 4; + data += 4; + pending -= 4; + } + + if (pending >= 2) + { + if (read) + *((unsigned short *) data) = inw (port + offset); + else + outw (*((unsigned short *) data), port + offset); + + offset += 2; + data += 2; + pending -= 2; + } + + if (pending) + { + if (read) + *((unsigned char *) data) = inb (port + offset); + else + outb (*((unsigned char *) data), port + offset); + + offset++; + data++; + pending--; + } + + *len -= pending; + + return 0; +} + +/* Read or write from/to a region file */ +error_t +io_region_file (struct pcifs_dirent * e, off_t offset, size_t * len, + void *data, int read) +{ + error_t err; + size_t reg_num; + struct pci_mem_region *region; + + /* This should never happen */ + assert_backtrace (e->device != 0); + + /* Get the region */ + reg_num = strtol (&e->name[strlen (e->name) - 1], 0, 16); + region = &e->device->regions[reg_num]; + + /* Refresh the region */ + err = pci_sys->device_refresh (e->device, reg_num, -1); + if (err) + return err; + + /* Don't exceed the region size */ + if (offset > region->size) + return EINVAL; + if ((offset + *len) > region->size) + *len = region->size - offset; + + if (region->is_IO) + region_block_ioport_op (region->base_addr, offset, len, data, read); + else if (read) + memcpy (data, region->memory + offset, *len); + else + memcpy (region->memory + offset, data, *len); + + return 0; +} |