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 | |
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>
-rw-r--r-- | Documentation/filesystems/sysfs-pci.txt | 7 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 68 |
2 files changed, 73 insertions, 2 deletions
diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 85354b32d731..74eaac26f8b8 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt | |||
@@ -39,7 +39,7 @@ files, each with their own function. | |||
39 | local_cpus nearby CPU mask (cpumask, ro) | 39 | local_cpus nearby CPU mask (cpumask, ro) |
40 | remove remove device from kernel's list (ascii, wo) | 40 | remove remove device from kernel's list (ascii, wo) |
41 | resource PCI resource host addresses (ascii, ro) | 41 | resource PCI resource host addresses (ascii, ro) |
42 | resource0..N PCI resource N, if present (binary, mmap) | 42 | resource0..N PCI resource N, if present (binary, mmap, rw[1]) |
43 | resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) | 43 | resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) |
44 | rom PCI ROM resource, if present (binary, ro) | 44 | rom PCI ROM resource, if present (binary, ro) |
45 | subsystem_device PCI subsystem device (ascii, ro) | 45 | subsystem_device PCI subsystem device (ascii, ro) |
@@ -54,13 +54,16 @@ files, each with their own function. | |||
54 | binary - file contains binary data | 54 | binary - file contains binary data |
55 | cpumask - file contains a cpumask type | 55 | cpumask - file contains a cpumask type |
56 | 56 | ||
57 | [1] rw for RESOURCE_IO (I/O port) regions only | ||
58 | |||
57 | The read only files are informational, writes to them will be ignored, with | 59 | The read only files are informational, writes to them will be ignored, with |
58 | the exception of the 'rom' file. Writable files can be used to perform | 60 | the exception of the 'rom' file. Writable files can be used to perform |
59 | actions on the device (e.g. changing config space, detaching a device). | 61 | actions on the device (e.g. changing config space, detaching a device). |
60 | mmapable files are available via an mmap of the file at offset 0 and can be | 62 | mmapable files are available via an mmap of the file at offset 0 and can be |
61 | used to do actual device programming from userspace. Note that some platforms | 63 | used to do actual device programming from userspace. Note that some platforms |
62 | don't support mmapping of certain resources, so be sure to check the return | 64 | don't support mmapping of certain resources, so be sure to check the return |
63 | value from any attempted mmap. | 65 | value from any attempted mmap. The most notable of these are I/O port |
66 | resources, which also provide read/write access. | ||
64 | 67 | ||
65 | The 'enable' file provides a counter that indicates how many times the device | 68 | The 'enable' file provides a counter that indicates how many times the device |
66 | has been enabled. If the 'enable' file currently returns '4', and a '1' is | 69 | has been enabled. If the 'enable' file currently returns '4', and a '1' is |
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); |