| /* | |
| * pci_syscall.c | |
| * | |
| * For architectures where we want to allow direct access | |
| * to the PCI config stuff - it would probably be preferable | |
| * on PCs too, but there people just do it by hand with the | |
| * magic northbridge registers.. | |
| */ | |
| #include <linux/errno.h> | |
| #include <linux/pci.h> | |
| #include <linux/syscalls.h> | |
| #include <asm/uaccess.h> | |
| #include "pci.h" | |
| SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn, | |
| unsigned long, off, unsigned long, len, void __user *, buf) | |
| { | |
| struct pci_dev *dev; | |
| u8 byte; | |
| u16 word; | |
| u32 dword; | |
| long err; | |
| long cfg_ret; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| err = -ENODEV; | |
| dev = pci_get_bus_and_slot(bus, dfn); | |
| if (!dev) | |
| goto error; | |
| switch (len) { | |
| case 1: | |
| cfg_ret = pci_user_read_config_byte(dev, off, &byte); | |
| break; | |
| case 2: | |
| cfg_ret = pci_user_read_config_word(dev, off, &word); | |
| break; | |
| case 4: | |
| cfg_ret = pci_user_read_config_dword(dev, off, &dword); | |
| break; | |
| default: | |
| err = -EINVAL; | |
| goto error; | |
| } | |
| err = -EIO; | |
| if (cfg_ret != PCIBIOS_SUCCESSFUL) | |
| goto error; | |
| switch (len) { | |
| case 1: | |
| err = put_user(byte, (unsigned char __user *)buf); | |
| break; | |
| case 2: | |
| err = put_user(word, (unsigned short __user *)buf); | |
| break; | |
| case 4: | |
| err = put_user(dword, (unsigned int __user *)buf); | |
| break; | |
| } | |
| pci_dev_put(dev); | |
| return err; | |
| error: | |
| /* ??? XFree86 doesn't even check the return value. They | |
| just look for 0xffffffff in the output, since that's what | |
| they get instead of a machine check on x86. */ | |
| switch (len) { | |
| case 1: | |
| put_user(-1, (unsigned char __user *)buf); | |
| break; | |
| case 2: | |
| put_user(-1, (unsigned short __user *)buf); | |
| break; | |
| case 4: | |
| put_user(-1, (unsigned int __user *)buf); | |
| break; | |
| } | |
| pci_dev_put(dev); | |
| return err; | |
| } | |
| SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, | |
| unsigned long, off, unsigned long, len, void __user *, buf) | |
| { | |
| struct pci_dev *dev; | |
| u8 byte; | |
| u16 word; | |
| u32 dword; | |
| int err = 0; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| dev = pci_get_bus_and_slot(bus, dfn); | |
| if (!dev) | |
| return -ENODEV; | |
| switch (len) { | |
| case 1: | |
| err = get_user(byte, (u8 __user *)buf); | |
| if (err) | |
| break; | |
| err = pci_user_write_config_byte(dev, off, byte); | |
| if (err != PCIBIOS_SUCCESSFUL) | |
| err = -EIO; | |
| break; | |
| case 2: | |
| err = get_user(word, (u16 __user *)buf); | |
| if (err) | |
| break; | |
| err = pci_user_write_config_word(dev, off, word); | |
| if (err != PCIBIOS_SUCCESSFUL) | |
| err = -EIO; | |
| break; | |
| case 4: | |
| err = get_user(dword, (u32 __user *)buf); | |
| if (err) | |
| break; | |
| err = pci_user_write_config_dword(dev, off, dword); | |
| if (err != PCIBIOS_SUCCESSFUL) | |
| err = -EIO; | |
| break; | |
| default: | |
| err = -EINVAL; | |
| break; | |
| } | |
| pci_dev_put(dev); | |
| return err; | |
| } |