syscall.c 2.61 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
/*
 *	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/sched.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>


asmlinkage long
sys_pciconfig_read(unsigned long bus, unsigned long dfn,
		   unsigned long off, unsigned long len, void *buf)
{
	struct pci_dev *dev;
	u8 byte;
	u16 word;
	u32 dword;
	long err, cfg_ret;

	err = -EPERM;
	if (!capable(CAP_SYS_ADMIN))
		goto error;

	err = -ENODEV;
	dev = pci_find_slot(bus, dfn);
	if (!dev)
		goto error;

	lock_kernel();
	switch (len) {
	case 1:
		cfg_ret = pci_read_config_byte(dev, off, &byte);
		break;
	case 2:
		cfg_ret = pci_read_config_word(dev, off, &word);
		break;
	case 4:
		cfg_ret = pci_read_config_dword(dev, off, &dword);
		break;
	default:
		err = -EINVAL;
		unlock_kernel();
		goto error;
	};
	unlock_kernel();

	err = -EIO;
	if (cfg_ret != PCIBIOS_SUCCESSFUL)
		goto error;

	switch (len) {
	case 1:
		err = put_user(byte, (unsigned char *)buf);
		break;
	case 2:
		err = put_user(word, (unsigned short *)buf);
		break;
	case 4:
		err = put_user(dword, (unsigned int *)buf);
		break;
	};
	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 *)buf);
		break;
	case 2:
		put_user(-1, (unsigned short *)buf);
		break;
	case 4:
		put_user(-1, (unsigned int *)buf);
		break;
	};
	return err;
}

asmlinkage long
sys_pciconfig_write(unsigned long bus, unsigned long dfn,
		    unsigned long off, unsigned long len, void *buf)
{
	struct pci_dev *dev;
	u8 byte;
	u16 word;
	u32 dword;
	int err = 0;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
101
	if (!pci_present())
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
		return -ENOSYS;

	dev = pci_find_slot(bus, dfn);
	if (!dev)
		return -ENODEV;

	lock_kernel();
	switch(len) {
	case 1:
		err = get_user(byte, (u8 *)buf);
		if (err)
			break;
		err = pci_write_config_byte(dev, off, byte);
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	case 2:
		err = get_user(word, (u16 *)buf);
		if (err)
			break;
		err = pci_write_config_word(dev, off, word);
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	case 4:
		err = get_user(dword, (u32 *)buf);
		if (err)
			break;
		err = pci_write_config_dword(dev, off, dword);
		if (err != PCIBIOS_SUCCESSFUL)
			err = -EIO;
		break;

	default:
		err = -EINVAL;
		break;
	};
	unlock_kernel();

	return err;
}