1 /* 2 * ISA Plug & Play support 3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 4 * 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 */ 21 22 #include <linux/module.h> 23 #include <linux/isapnp.h> 24 #include <linux/proc_fs.h> 25 #include <linux/init.h> 26 #include <linux/smp_lock.h> 27 #include <asm/uaccess.h> 28 29 extern struct pnp_protocol isapnp_protocol; 30 31 static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; 32 33 static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) 34 { 35 loff_t new = -1; 36 37 lock_kernel(); 38 switch (whence) { 39 case 0: 40 new = off; 41 break; 42 case 1: 43 new = file->f_pos + off; 44 break; 45 case 2: 46 new = 256 + off; 47 break; 48 } 49 if (new < 0 || new > 256) { 50 unlock_kernel(); 51 return -EINVAL; 52 } 53 unlock_kernel(); 54 return (file->f_pos = new); 55 } 56 57 static ssize_t isapnp_proc_bus_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 58 { 59 struct inode *ino = file->f_path.dentry->d_inode; 60 struct proc_dir_entry *dp = PDE(ino); 61 struct pnp_dev *dev = dp->data; 62 int pos = *ppos; 63 int cnt, size = 256; 64 65 if (pos >= size) 66 return 0; 67 if (nbytes >= size) 68 nbytes = size; 69 if (pos + nbytes > size) 70 nbytes = size - pos; 71 cnt = nbytes; 72 73 if (!access_ok(VERIFY_WRITE, buf, cnt)) 74 return -EINVAL; 75 76 isapnp_cfg_begin(dev->card->number, dev->number); 77 for ( ; pos < 256 && cnt > 0; pos++, buf++, cnt--) { 78 unsigned char val; 79 val = isapnp_read_byte(pos); 80 __put_user(val, buf); 81 } 82 isapnp_cfg_end(); 83 84 *ppos = pos; 85 return nbytes; 86 } 87 88 static const struct file_operations isapnp_proc_bus_file_operations = 89 { 90 .llseek = isapnp_proc_bus_lseek, 91 .read = isapnp_proc_bus_read, 92 }; 93 94 static int isapnp_proc_attach_device(struct pnp_dev *dev) 95 { 96 struct pnp_card *bus = dev->card; 97 struct proc_dir_entry *de, *e; 98 char name[16]; 99 100 if (!(de = bus->procdir)) { 101 sprintf(name, "%02x", bus->number); 102 de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir); 103 if (!de) 104 return -ENOMEM; 105 } 106 sprintf(name, "%02x", dev->number); 107 e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de); 108 if (!e) 109 return -ENOMEM; 110 e->proc_fops = &isapnp_proc_bus_file_operations; 111 e->owner = THIS_MODULE; 112 e->data = dev; 113 e->size = 256; 114 return 0; 115 } 116 117 #ifdef MODULE 118 static int __exit isapnp_proc_detach_device(struct pnp_dev *dev) 119 { 120 struct pnp_card *bus = dev->card; 121 struct proc_dir_entry *de; 122 char name[16]; 123 124 if (!(de = bus->procdir)) 125 return -EINVAL; 126 sprintf(name, "%02x", dev->number); 127 remove_proc_entry(name, de); 128 return 0; 129 } 130 131 static int __exit isapnp_proc_detach_bus(struct pnp_card *bus) 132 { 133 struct proc_dir_entry *de; 134 char name[16]; 135 136 if (!(de = bus->procdir)) 137 return -EINVAL; 138 sprintf(name, "%02x", bus->number); 139 remove_proc_entry(name, isapnp_proc_bus_dir); 140 return 0; 141 } 142 #endif /* MODULE */ 143 144 int __init isapnp_proc_init(void) 145 { 146 struct pnp_dev *dev; 147 isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus); 148 protocol_for_each_dev(&isapnp_protocol,dev) { 149 isapnp_proc_attach_device(dev); 150 } 151 return 0; 152 } 153 154 #ifdef MODULE 155 int __exit isapnp_proc_done(void) 156 { 157 struct pnp_dev *dev; 158 struct pnp_bus *card; 159 160 isapnp_for_each_dev(dev) { 161 isapnp_proc_detach_device(dev); 162 } 163 isapnp_for_each_card(card) { 164 isapnp_proc_detach_bus(card); 165 } 166 if (isapnp_proc_bus_dir) 167 remove_proc_entry("isapnp", proc_bus); 168 return 0; 169 } 170 #endif /* MODULE */ 171