diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2010-07-19 11:45:34 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-07-30 12:32:08 -0400 |
commit | 8633328be242677fdedc42052838dd0608e7f342 (patch) | |
tree | 20b16af605298a1d05973d7ec2d0a479412ed3e6 /drivers/pci | |
parent | 2491762cfb475dbdfa3db11ebea6de49f58b7fac (diff) |
PCI: Allow read/write access to sysfs I/O port resources
PCI sysfs resource files currently only allow mmap'ing. On x86 this
works fine for memory backed BARs, but doesn't work at all for I/O
port backed BARs. Add read/write to I/O port PCI sysfs resource
files to allow userspace access to these device regions.
Acked-by: Chris Wright <chrisw@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5935b854917f..f7692dc531e4 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -778,6 +778,70 @@ pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, | |||
778 | return pci_mmap_resource(kobj, attr, vma, 1); | 778 | return pci_mmap_resource(kobj, attr, vma, 1); |
779 | } | 779 | } |
780 | 780 | ||
781 | static ssize_t | ||
782 | pci_resource_io(struct file *filp, struct kobject *kobj, | ||
783 | struct bin_attribute *attr, char *buf, | ||
784 | loff_t off, size_t count, bool write) | ||
785 | { | ||
786 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, | ||
787 | struct device, kobj)); | ||
788 | struct resource *res = attr->private; | ||
789 | unsigned long port = off; | ||
790 | int i; | ||
791 | |||
792 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | ||
793 | if (res == &pdev->resource[i]) | ||
794 | break; | ||
795 | if (i >= PCI_ROM_RESOURCE) | ||
796 | return -ENODEV; | ||
797 | |||
798 | port += pci_resource_start(pdev, i); | ||
799 | |||
800 | if (port > pci_resource_end(pdev, i)) | ||
801 | return 0; | ||
802 | |||
803 | if (port + count - 1 > pci_resource_end(pdev, i)) | ||
804 | return -EINVAL; | ||
805 | |||
806 | switch (count) { | ||
807 | case 1: | ||
808 | if (write) | ||
809 | outb(*(u8 *)buf, port); | ||
810 | else | ||
811 | *(u8 *)buf = inb(port); | ||
812 | return 1; | ||
813 | case 2: | ||
814 | if (write) | ||
815 | outw(*(u16 *)buf, port); | ||
816 | else | ||
817 | *(u16 *)buf = inw(port); | ||
818 | return 2; | ||
819 | case 4: | ||
820 | if (write) | ||
821 | outl(*(u32 *)buf, port); | ||
822 | else | ||
823 | *(u32 *)buf = inl(port); | ||
824 | return 4; | ||
825 | } | ||
826 | return -EINVAL; | ||
827 | } | ||
828 | |||
829 | static ssize_t | ||
830 | pci_read_resource_io(struct file *filp, struct kobject *kobj, | ||
831 | struct bin_attribute *attr, char *buf, | ||
832 | loff_t off, size_t count) | ||
833 | { | ||
834 | return pci_resource_io(filp, kobj, attr, buf, off, count, false); | ||
835 | } | ||
836 | |||
837 | static ssize_t | ||
838 | pci_write_resource_io(struct file *filp, struct kobject *kobj, | ||
839 | struct bin_attribute *attr, char *buf, | ||
840 | loff_t off, size_t count) | ||
841 | { | ||
842 | return pci_resource_io(filp, kobj, attr, buf, off, count, true); | ||
843 | } | ||
844 | |||
781 | /** | 845 | /** |
782 | * pci_remove_resource_files - cleanup resource files | 846 | * pci_remove_resource_files - cleanup resource files |
783 | * @pdev: dev to cleanup | 847 | * @pdev: dev to cleanup |
@@ -828,6 +892,10 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) | |||
828 | sprintf(res_attr_name, "resource%d", num); | 892 | sprintf(res_attr_name, "resource%d", num); |
829 | res_attr->mmap = pci_mmap_resource_uc; | 893 | res_attr->mmap = pci_mmap_resource_uc; |
830 | } | 894 | } |
895 | if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { | ||
896 | res_attr->read = pci_read_resource_io; | ||
897 | res_attr->write = pci_write_resource_io; | ||
898 | } | ||
831 | res_attr->attr.name = res_attr_name; | 899 | res_attr->attr.name = res_attr_name; |
832 | res_attr->attr.mode = S_IRUSR | S_IWUSR; | 900 | res_attr->attr.mode = S_IRUSR | S_IWUSR; |
833 | res_attr->size = pci_resource_len(pdev, num); | 901 | res_attr->size = pci_resource_len(pdev, num); |